Flutter Explained — Cross-Platform Mobile Development with Dart
Flutter is Google’s UI toolkit for building natively compiled applications for mobile, web, and desktop from a single codebase using the Dart programming language and a unique widget-based architecture.
What You’ll Learn
You’ll understand Flutter’s widget tree, manage state with setState, use hot reload for rapid development, and build a cross-platform app using Dart code examples with expected output.
Why Flutter Matters
Flutter is one of the fastest-growing cross-platform frameworks. It’s used by Google (Google Pay), Alibaba, and BMW. At DodaTech, we’re exploring Flutter for our mobile security scanner interface because it renders at 60fps on both platforms and produces visually consistent UIs. Unlike React Native which bridges to native components, Flutter draws everything itself — giving pixel-perfect control.
Flutter Learning Path
flowchart LR
A[Dart Basics] --> B[Flutter]
B --> C[Widgets & Layouts]
C --> D[State Management]
D --> E[Navigation]
E --> F[Networking & APIs]
F --> G[Platform Channels]
G --> H[App Store & Play Store]
B:::current
classDef current fill:#f90,color:#fff,stroke:#333,stroke-width:2px
How Flutter Is Different
Most cross-platform frameworks (like React Native) use a “bridge” to communicate with native UI components. Flutter does something completely different: it draws every pixel on the screen using its own rendering engine called Skia.
Think of it this way:
- React Native is like ordering from two different restaurants (Android and iOS kitchens) using a translator. The translator might get things wrong or be slow.
- Flutter is like having your own kitchen with its own ingredients. You cook the same meal everywhere, and it tastes identical on both platforms.
This approach means Flutter apps look and feel the same on Android and iOS, which is great for brand consistency. The trade-off is larger app sizes (Flutter includes its own engine in the binary).
Everything Is a Widget
In Flutter, everything is a widget. The text you see is a widget. The padding around it is a widget. The row containing those elements is a widget. Even your entire app is a widget.
Widgets form a tree structure:
graph TD
A[MyApp - MaterialApp] --> B[Scaffold]
B --> C[AppBar]
B --> D[Center]
D --> E[Column]
E --> F[Text 'Hello']
E --> G[SizedBox - spacing]
E --> H[ElevatedButton]
Let’s build a simple Flutter app step by step.
Step 1: Create a New Project
flutter create hello_dodatech
cd hello_dodatech
flutter runThis creates a counter app template. Let’s replace it with our own app.
Step 2: Understanding the Main Entry Point
// lib/main.dart
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}Every Flutter app starts with main(), which calls runApp(). runApp takes the root widget of your application and mounts it to the screen.
Step 3: Build the App Widget
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Hello DodaTech',
theme: ThemeData(
colorSchemeSeed: Colors.teal,
useMaterial3: true,
),
home: const HomeScreen(),
);
}
}Stateless vs Stateful: A StatelessWidget is immutable — once built, it never changes. A StatefulWidget can update dynamically.
MaterialApp sets up Material Design theming. It’s like the stage for your app — it provides navigation, theming, and localization.
Step 4: Create the Home Screen
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
// State variables
final TextEditingController _nameController = TextEditingController();
String _greeting = 'Welcome to Flutter!';
final List<String> _items = [
'Learn Flutter Widgets',
'Build an App',
'Master State Management',
'Publish to Stores',
];
@override
void dispose() {
_nameController.dispose();
super.dispose();
}
void _onGreet() {
setState(() {
final name = _nameController.text.trim();
if (name.isEmpty) {
_greeting = 'Please enter a name!';
} else {
_greeting = 'Hello, $name!';
}
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Hello DodaTech'),
backgroundColor: Colors.teal,
foregroundColor: Colors.white,
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Greeting text
Text(
_greeting,
style: Theme.of(context).textTheme.headlineSmall,
textAlign: TextAlign.center,
),
const SizedBox(height: 16),
// Text input field
TextField(
controller: _nameController,
decoration: const InputDecoration(
labelText: 'Enter your name',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.person),
),
),
const SizedBox(height: 16),
// Greet button
ElevatedButton(
onPressed: _onGreet,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.teal,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 16),
),
child: const Text('Greet Me'),
),
const SizedBox(height: 24),
// List header
Text(
'Learning Roadmap:',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 8),
// List of items
Expanded(
child: ListView.builder(
itemCount: _items.length,
itemBuilder: (context, index) {
return Card(
child: ListTile(
leading: CircleAvatar(
backgroundColor: Colors.teal.shade100,
child: Text('${index + 1}'),
),
title: Text(_items[index]),
trailing: const Icon(Icons.arrow_forward_ios),
),
);
},
),
),
],
),
),
);
}
}Breaking down the code:
StatefulWidgetandState— Together they create a widget that can update. The widget itself is immutable (configuration), but theStateobject persists and can changeTextEditingController— Manages the text input. Think of it as a bridge between the TextField widget and your code. We calldispose()on it when the screen is destroyed to prevent memory leakssetState()— This is Flutter’s way of saying “rebuild this widget with new data.” CallingsetStatetriggers thebuild()method again, updating the UIScaffold— Provides the basic Material Design layout structure (app bar, body, bottom navigation)ListView.builder— Efficiently builds a scrollable list. It only creates widgets for visible items, like recycling cells in Android/iOSSizedBox— A simple widget that adds fixed space between other widgets
Expected Output
When you run flutter run:
- An app with a teal AppBar showing “Hello DodaTech”
- “Welcome to Flutter!” centered below
- A text field with a person icon and “Enter your name” label
- A “Greet Me” button
- Below that, a numbered card list with the four learning items
- Each card has a forward arrow icon on the right
- When you type a name and tap “Greet Me”, the greeting text updates to “Hello, [Name]!”
Navigation: Adding a Second Screen
Flutter uses a Navigator widget to manage screens (called “routes”). Let’s add a detail screen:
// Add to main.dart or create a new file
class ProfileScreen extends StatelessWidget {
final String userName;
const ProfileScreen({super.key, required this.userName});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Your Profile'),
backgroundColor: Colors.teal,
foregroundColor: Colors.white,
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(
Icons.account_circle,
size: 100,
color: Colors.teal,
),
const SizedBox(height: 24),
Text(
'Hello, $userName!',
style: Theme.of(context).textTheme.headlineMedium,
),
const SizedBox(height: 16),
ElevatedButton(
onPressed: () {
Navigator.pop(context); // Go back to home
},
child: const Text('Go Back'),
),
],
),
),
);
}
}To navigate from HomeScreen, replace the ElevatedButton’s onPressed:
onPressed: () {
final name = _nameController.text.trim();
if (name.isEmpty) {
setState(() => _greeting = 'Please enter a name!');
} else {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProfileScreen(userName: name),
),
);
}
},How navigation works:
Navigator.push()adds a new route to the navigation stack (like placing a new card on top of a deck)MaterialPageRoutedefines the transition animation and the screen to showNavigator.pop()removes the current route and returns to the previous screen
Hot Reload — Your Superpower
Flutter’s hot reload is one of its best features. When you edit your code and save (or press r in the terminal), Flutter rebuilds the widget tree in milliseconds — without losing the app’s state.
Imagine editing a document and seeing your changes appear instantly without closing and reopening the file. That’s hot reload.
Try it now: change the “Welcome to Flutter!” greeting to “Flutter is Amazing!” and save. The screen updates instantly.
Security Angle: Input Validation
In our Flutter code, we use _nameController.text.trim() to clean user input. For production Flutter apps:
- Use
TextFormFieldwith avalidatorproperty for form validation - Never store sensitive data in plain SharedPreferences — use
flutter_secure_storage - Sanitize text before displaying it to prevent injection in WebViews
- Use
httppackage with certificate pinning for secure API calls - Check that URLs start with
https://before making network requests
DodaTech’s security scanner tools use similar text validation patterns when analyzing file names for suspicious patterns.
Common Mistakes Beginners Make
- Calling
setStateafterdispose: If you have async operations, checkmountedbefore callingsetState. - Not disposing controllers:
TextEditingController,AnimationController, andScrollControllermust be disposed to prevent memory leaks. - Using
ColumnwithoutExpandedfor scrollable content: If content exceeds the screen, wrap it inExpanded+SingleChildScrollView. - Forgetting the
constkeyword: Flutter widgets are immutable. Useconstconstructors where possible for performance. - Deep widget nesting: Deeply nested widgets hurt readability. Extract widgets into separate methods or classes.
- Not handling null safety: Dart is null-safe. Use
?for nullable types and!only when you’re absolutely sure. - Blocking the UI thread: Heavy computations should use
compute()(Flutter’s isolate function) to run on a separate thread.
Practice Questions
- What is the difference between StatelessWidget and StatefulWidget?
- What does
setState()do? - How do you navigate to a new screen in Flutter?
- What is the widget at the root of every Material Design Flutter app?
- Why must you dispose TextEditingController?
Answers:
- StatelessWidget never changes after building; StatefulWidget can update dynamically using setState.
- It marks the widget as needing rebuild, triggering the build() method with updated state.
- Use
Navigator.push(context, MaterialPageRoute(builder: ...)). MaterialApp— it provides theming, navigation, and Material Design structure.- To free resources and prevent memory leaks. Controllers hold references that need cleanup.
Challenge
Add a “Favorites” feature. Each card in the list should have a heart icon on the right. When tapped, it toggles between filled (favorited) and outlined (not favorited) state. Use a Set<int> to track which indices are favorited.
Real-World Task
Build a “Quote of the Day” app. Display a random quote from a predefined list. Include a button to generate a new random quote. Add a second screen that shows all quotes in a scrollable list. Style it with a clean, modern design using Flutter’s ThemeData.
Featured Snippet
What is Flutter?
Flutter is Google’s open-source UI toolkit that builds natively compiled applications for mobile, web, and desktop from a single Dart codebase, using a widget-based architecture and its own Skia rendering engine for consistent cross-platform performance.
FAQ
Try It Yourself
What’s Next
What’s Next
Congratulations on completing this Flutter tutorial! Here’s where to go from here:
- Practice daily — Consistency is more important than long study sessions
- Build a project — Apply what you learned by building something real
- Explore related topics — Check out other tutorials in the same category
- Join the community — Discuss with other learners and share your progress
Remember: every expert was once a beginner. Keep coding!
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro