Dart & Flutter Expert
Expert guidance for Dart programming, Flutter framework, mobile development, and cross-platform applications.
Core Concepts
Dart Language
- Strong typing with type inference
- Async/await and Futures
- Streams
- Mixins and extensions
- Null safety
- Collections
Flutter Framework
- Widgets (Stateless & Stateful)
- State management (Provider, Riverpod, Bloc)
- Navigation and routing
- Material and Cupertino design
- Responsive layouts
- Platform integration
Dart Fundamentals
dart
1// Variables and types
2var name = 'John'; // Type inference
3String explicitType = 'Explicit';
4final constantValue = 42; // Runtime constant
5const compileConstant = 'Compile-time';
6
7// Null safety
8String? nullableString;
9String nonNullable = 'Never null';
10
11// Late initialization
12late String lateInit;
13
14// Collections
15List<String> names = ['Alice', 'Bob', 'Charlie'];
16Map<String, int> ages = {'Alice': 25, 'Bob': 30};
17Set<int> uniqueNumbers = {1, 2, 3, 3}; // {1, 2, 3}
18
19// Functions
20int add(int a, int b) => a + b;
21
22// Named parameters
23void createUser({
24 required String name,
25 int age = 18,
26 String? email,
27}) {
28 print('Creating user: $name, age: $age');
29}
30
31// Classes
32class User {
33 final String id;
34 final String name;
35 int age;
36
37 User({
38 required this.id,
39 required this.name,
40 this.age = 0,
41 });
42
43 // Named constructor
44 User.guest() : this(id: 'guest', name: 'Guest');
45
46 // Methods
47 void celebrateBirthday() {
48 age++;
49 }
50
51 @override
52 String toString() => 'User($name, $age)';
53}
54
55// Mixins
56mixin Loggable {
57 void log(String message) {
58 print('[${DateTime.now()}] $message');
59 }
60}
61
62class LoggableUser extends User with Loggable {
63 LoggableUser({required String id, required String name})
64 : super(id: id, name: name);
65}
66
67// Extensions
68extension StringExtensions on String {
69 bool get isValidEmail => contains('@') && contains('.');
70 String capitalize() => '${this[0].toUpperCase()}${substring(1)}';
71}
Async Programming
dart
1import 'dart:async';
2
3// Future
4Future<String> fetchUser(String id) async {
5 await Future.delayed(Duration(seconds: 1));
6 return 'User $id';
7}
8
9// Error handling
10Future<User?> fetchUserSafely(String id) async {
11 try {
12 final user = await fetchUser(id);
13 return User(id: id, name: user);
14 } catch (e) {
15 print('Error: $e');
16 return null;
17 }
18}
19
20// Multiple futures
21Future<void> fetchMultiple() async {
22 final results = await Future.wait([
23 fetchUser('1'),
24 fetchUser('2'),
25 fetchUser('3'),
26 ]);
27 print(results);
28}
29
30// Streams
31Stream<int> countStream() async* {
32 for (int i = 1; i <= 5; i++) {
33 await Future.delayed(Duration(seconds: 1));
34 yield i;
35 }
36}
37
38// Listen to stream
39void listenToStream() {
40 countStream().listen(
41 (data) => print('Received: $data'),
42 onError: (error) => print('Error: $error'),
43 onDone: () => print('Done'),
44 );
45}
46
47// Stream transformations
48Stream<String> transformedStream() {
49 return countStream()
50 .where((n) => n % 2 == 0)
51 .map((n) => 'Number: $n')
52 .take(3);
53}
54
55// StreamController
56class Counter {
57 final _controller = StreamController<int>();
58
59 Stream<int> get stream => _controller.stream;
60
61 void increment(int value) {
62 _controller.add(value);
63 }
64
65 void dispose() {
66 _controller.close();
67 }
68}
dart
1import 'package:flutter/material.dart';
2
3// Stateless Widget
4class UserCard extends StatelessWidget {
5 final User user;
6
7 const UserCard({Key? key, required this.user}) : super(key: key);
8
9 @override
10 Widget build(BuildContext context) {
11 return Card(
12 child: ListTile(
13 title: Text(user.name),
14 subtitle: Text('Age: ${user.age}'),
15 trailing: Icon(Icons.arrow_forward),
16 ),
17 );
18 }
19}
20
21// Stateful Widget
22class CounterWidget extends StatefulWidget {
23 const CounterWidget({Key? key}) : super(key: key);
24
25 @override
26 State<CounterWidget> createState() => _CounterWidgetState();
27}
28
29class _CounterWidgetState extends State<CounterWidget> {
30 int _counter = 0;
31
32 void _incrementCounter() {
33 setState(() {
34 _counter++;
35 });
36 }
37
38 @override
39 Widget build(BuildContext context) {
40 return Column(
41 children: [
42 Text('Counter: $_counter'),
43 ElevatedButton(
44 onPressed: _incrementCounter,
45 child: Text('Increment'),
46 ),
47 ],
48 );
49 }
50}
51
52// Layout
53class UserListScreen extends StatelessWidget {
54 final List<User> users;
55
56 const UserListScreen({Key? key, required this.users}) : super(key: key);
57
58 @override
59 Widget build(BuildContext context) {
60 return Scaffold(
61 appBar: AppBar(
62 title: Text('Users'),
63 actions: [
64 IconButton(
65 icon: Icon(Icons.add),
66 onPressed: () => Navigator.push(
67 context,
68 MaterialPageRoute(builder: (_) => AddUserScreen()),
69 ),
70 ),
71 ],
72 ),
73 body: ListView.builder(
74 itemCount: users.length,
75 itemBuilder: (context, index) {
76 return UserCard(user: users[index]);
77 },
78 ),
79 );
80 }
81}
State Management (Provider)
dart
1import 'package:provider/provider.dart';
2
3// State class
4class UserProvider extends ChangeNotifier {
5 List<User> _users = [];
6
7 List<User> get users => _users;
8
9 Future<void> fetchUsers() async {
10 _users = await api.getUsers();
11 notifyListeners();
12 }
13
14 void addUser(User user) {
15 _users.add(user);
16 notifyListeners();
17 }
18
19 void removeUser(String id) {
20 _users.removeWhere((user) => user.id == id);
21 notifyListeners();
22 }
23}
24
25// Setup provider
26void main() {
27 runApp(
28 MultiProvider(
29 providers: [
30 ChangeNotifierProvider(create: (_) => UserProvider()),
31 ChangeNotifierProvider(create: (_) => AuthProvider()),
32 ],
33 child: MyApp(),
34 ),
35 );
36}
37
38// Consume provider
39class UserList extends StatelessWidget {
40 @override
41 Widget build(BuildContext context) {
42 final userProvider = Provider.of<UserProvider>(context);
43
44 return ListView.builder(
45 itemCount: userProvider.users.length,
46 itemBuilder: (context, index) {
47 final user = userProvider.users[index];
48 return ListTile(
49 title: Text(user.name),
50 trailing: IconButton(
51 icon: Icon(Icons.delete),
52 onPressed: () => userProvider.removeUser(user.id),
53 ),
54 );
55 },
56 );
57 }
58}
59
60// Consumer widget
61class UserCounter extends StatelessWidget {
62 @override
63 Widget build(BuildContext context) {
64 return Consumer<UserProvider>(
65 builder: (context, userProvider, child) {
66 return Text('Total users: ${userProvider.users.length}');
67 },
68 );
69 }
70}
Navigation
dart
1// Basic navigation
2Navigator.push(
3 context,
4 MaterialPageRoute(builder: (context) => DetailScreen()),
5);
6
7// Named routes
8MaterialApp(
9 routes: {
10 '/': (context) => HomeScreen(),
11 '/details': (context) => DetailScreen(),
12 '/settings': (context) => SettingsScreen(),
13 },
14);
15
16Navigator.pushNamed(context, '/details');
17
18// Pass arguments
19Navigator.pushNamed(
20 context,
21 '/details',
22 arguments: {'userId': '123'},
23);
24
25// Get arguments
26final args = ModalRoute.of(context)!.settings.arguments as Map;
27
28// Return data
29final result = await Navigator.push(
30 context,
31 MaterialPageRoute(builder: (_) => SelectionScreen()),
32);
33
34// Pop with result
35Navigator.pop(context, selectedValue);
HTTP and API
dart
1import 'package:http/http.dart' as http;
2import 'dart:convert';
3
4class ApiService {
5 final String baseUrl = 'https://api.example.com';
6
7 Future<List<User>> fetchUsers() async {
8 final response = await http.get(Uri.parse('$baseUrl/users'));
9
10 if (response.statusCode == 200) {
11 final List<dynamic> data = json.decode(response.body);
12 return data.map((json) => User.fromJson(json)).toList();
13 } else {
14 throw Exception('Failed to load users');
15 }
16 }
17
18 Future<User> createUser(User user) async {
19 final response = await http.post(
20 Uri.parse('$baseUrl/users'),
21 headers: {'Content-Type': 'application/json'},
22 body: json.encode(user.toJson()),
23 );
24
25 if (response.statusCode == 201) {
26 return User.fromJson(json.decode(response.body));
27 } else {
28 throw Exception('Failed to create user');
29 }
30 }
31}
32
33// Model with JSON serialization
34class User {
35 final String id;
36 final String name;
37 final String email;
38
39 User({required this.id, required this.name, required this.email});
40
41 factory User.fromJson(Map<String, dynamic> json) {
42 return User(
43 id: json['id'],
44 name: json['name'],
45 email: json['email'],
46 );
47 }
48
49 Map<String, dynamic> toJson() {
50 return {
51 'id': id,
52 'name': name,
53 'email': email,
54 };
55 }
56}
Best Practices
Dart
- Use const constructors when possible
- Leverage null safety
- Use final for immutable values
- Prefer async/await over .then()
- Use extensions for utility methods
- Follow Effective Dart guidelines
Flutter
- Keep widgets small and focused
- Use const widgets for optimization
- Avoid rebuilding entire trees
- Implement proper state management
- Handle errors gracefully
- Test widgets thoroughly
- Use keys when needed
- Use ListView.builder for long lists
- Implement pagination
- Cache network images
- Minimize widget rebuilds
- Use const constructors
- Profile app performance
- Lazy load resources
Anti-Patterns
❌ Large widget trees
❌ State in StatelessWidget
❌ Not disposing controllers
❌ Ignoring null safety
❌ Synchronous I/O in UI
❌ No error handling
❌ Hard-coded strings
Resources