Flutter Development
Overview
Create high-performance, visually stunning mobile applications using Flutter with Dart language. Master widget composition, state management patterns, navigation, and API integration.
When to Use
- Building iOS and Android apps with native performance
- Designing custom UIs with Flutter's widget system
- Implementing complex animations and visual effects
- Rapid app development with hot reload
- Creating consistent UX across platforms
Instructions
1. Project Structure & Navigation
dart
1// pubspec.yaml
2name: my_flutter_app
3version: 1.0.0
4
5dependencies:
6 flutter:
7 sdk: flutter
8 provider: ^6.0.0
9 http: ^1.1.0
10 go_router: ^12.0.0
11
12// main.dart with GoRouter navigation
13import 'package:flutter/material.dart';
14import 'package:go_router/go_router.dart';
15
16void main() {
17 runApp(const MyApp());
18}
19
20class MyApp extends StatelessWidget {
21 const MyApp({Key? key}) : super(key: key);
22
23 @override
24 Widget build(BuildContext context) {
25 return MaterialApp.router(
26 title: 'Flutter App',
27 theme: ThemeData(primarySwatch: Colors.blue, useMaterial3: true),
28 routerConfig: _router,
29 );
30 }
31}
32
33final GoRouter _router = GoRouter(
34 routes: <RouteBase>[
35 GoRoute(
36 path: '/',
37 builder: (context, state) => const HomeScreen(),
38 routes: [
39 GoRoute(
40 path: 'details/:id',
41 builder: (context, state) => DetailsScreen(
42 itemId: state.pathParameters['id']!
43 ),
44 ),
45 ],
46 ),
47 GoRoute(
48 path: '/profile',
49 builder: (context, state) => const ProfileScreen(),
50 ),
51 ],
52);
2. State Management with Provider
dart
1import 'package:flutter/material.dart';
2import 'package:http/http.dart' as http;
3import 'dart:convert';
4
5class User {
6 final String id;
7 final String name;
8 final String email;
9
10 User({required this.id, required this.name, required this.email});
11
12 factory User.fromJson(Map<String, dynamic> json) {
13 return User(
14 id: json['id'],
15 name: json['name'],
16 email: json['email'],
17 );
18 }
19}
20
21class UserProvider extends ChangeNotifier {
22 User? _user;
23 bool _isLoading = false;
24 String? _error;
25
26 User? get user => _user;
27 bool get isLoading => _isLoading;
28 String? get error => _error;
29
30 Future<void> fetchUser(String userId) async {
31 _isLoading = true;
32 _error = null;
33 notifyListeners();
34
35 try {
36 final response = await http.get(
37 Uri.parse('https://api.example.com/users/$userId'),
38 headers: {'Content-Type': 'application/json'},
39 );
40
41 if (response.statusCode == 200) {
42 _user = User.fromJson(jsonDecode(response.body));
43 } else {
44 _error = 'Failed to fetch user';
45 }
46 } catch (e) {
47 _error = 'Error: ${e.toString()}';
48 } finally {
49 _isLoading = false;
50 notifyListeners();
51 }
52 }
53
54 void logout() {
55 _user = null;
56 notifyListeners();
57 }
58}
59
60class ItemsProvider extends ChangeNotifier {
61 List<Map<String, dynamic>> _items = [];
62
63 List<Map<String, dynamic>> get items => _items;
64
65 Future<void> fetchItems() async {
66 try {
67 final response = await http.get(
68 Uri.parse('https://api.example.com/items'),
69 );
70
71 if (response.statusCode == 200) {
72 _items = List<Map<String, dynamic>>.from(
73 jsonDecode(response.body) as List
74 );
75 notifyListeners();
76 }
77 } catch (e) {
78 print('Error fetching items: $e');
79 }
80 }
81}
3. Screens with Provider Integration
dart
1class HomeScreen extends StatefulWidget {
2 const HomeScreen({Key? key}) : super(key: key);
3
4 @override
5 State<HomeScreen> createState() => _HomeScreenState();
6}
7
8class _HomeScreenState extends State<HomeScreen> {
9 @override
10 void initState() {
11 super.initState();
12 Future.microtask(() {
13 Provider.of<ItemsProvider>(context, listen: false).fetchItems();
14 });
15 }
16
17 @override
18 Widget build(BuildContext context) {
19 return Scaffold(
20 appBar: AppBar(title: const Text('Home Feed')),
21 body: Consumer<ItemsProvider>(
22 builder: (context, itemsProvider, child) {
23 if (itemsProvider.items.isEmpty) {
24 return const Center(child: Text('No items found'));
25 }
26 return ListView.builder(
27 itemCount: itemsProvider.items.length,
28 itemBuilder: (context, index) {
29 final item = itemsProvider.items[index];
30 return ItemCard(item: item);
31 },
32 );
33 },
34 ),
35 );
36 }
37}
38
39class ItemCard extends StatelessWidget {
40 final Map<String, dynamic> item;
41
42 const ItemCard({required this.item, Key? key}) : super(key: key);
43
44 @override
45 Widget build(BuildContext context) {
46 return Card(
47 margin: const EdgeInsets.all(8),
48 child: ListTile(
49 title: Text(item['title'] ?? 'Untitled'),
50 subtitle: Text(item['description'] ?? ''),
51 trailing: const Icon(Icons.arrow_forward),
52 onTap: () => context.go('/details/${item['id']}'),
53 ),
54 );
55 }
56}
57
58class DetailsScreen extends StatelessWidget {
59 final String itemId;
60
61 const DetailsScreen({required this.itemId, Key? key}) : super(key: key);
62
63 @override
64 Widget build(BuildContext context) {
65 return Scaffold(
66 appBar: AppBar(title: const Text('Details')),
67 body: Center(
68 child: Column(
69 mainAxisAlignment: MainAxisAlignment.center,
70 children: [
71 Text('Item ID: $itemId', style: const TextStyle(fontSize: 18)),
72 const SizedBox(height: 16),
73 ElevatedButton(
74 onPressed: () => context.pop(),
75 child: const Text('Go Back'),
76 ),
77 ],
78 ),
79 ),
80 );
81 }
82}
83
84class ProfileScreen extends StatelessWidget {
85 const ProfileScreen({Key? key}) : super(key: key);
86
87 @override
88 Widget build(BuildContext context) {
89 return Scaffold(
90 appBar: AppBar(title: const Text('Profile')),
91 body: Consumer<UserProvider>(
92 builder: (context, userProvider, child) {
93 if (userProvider.isLoading) {
94 return const Center(child: CircularProgressIndicator());
95 }
96 if (userProvider.error != null) {
97 return Center(child: Text('Error: ${userProvider.error}'));
98 }
99 final user = userProvider.user;
100 if (user == null) {
101 return const Center(child: Text('No user data'));
102 }
103 return Padding(
104 padding: const EdgeInsets.all(16),
105 child: Column(
106 crossAxisAlignment: CrossAxisAlignment.start,
107 children: [
108 Text('Name: ${user.name}', style: const TextStyle(fontSize: 18)),
109 Text('Email: ${user.email}', style: const TextStyle(fontSize: 16)),
110 const SizedBox(height: 16),
111 ElevatedButton(
112 onPressed: () => userProvider.logout(),
113 child: const Text('Logout'),
114 ),
115 ],
116 ),
117 );
118 },
119 ),
120 );
121 }
122}
Best Practices
✅ DO
- Use widgets for every UI element
- Implement proper state management
- Use const constructors where possible
- Dispose resources in state lifecycle
- Test on multiple device sizes
- Use meaningful widget names
- Implement error handling
- Use responsive design patterns
- Test on both iOS and Android
- Document custom widgets
❌ DON'T
- Build entire screens in build() method
- Use setState for complex state logic
- Make network calls in build()
- Ignore platform differences
- Create overly nested widget trees
- Hardcode strings
- Ignore performance warnings
- Skip testing
- Forget to handle edge cases
- Deploy without thorough testing