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
dart1// 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
dart1import '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}
Flutter Widgets
dart1import '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)
dart1import '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
dart1// 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
dart1import '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
Performance
- 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
- Dart: https://dart.dev/
- Flutter: https://flutter.dev/
- Pub.dev: https://pub.dev/
- Flutter Widget Catalog: https://flutter.dev/docs/development/ui/widgets
- Effective Dart: https://dart.dev/guides/language/effective-dart