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
- Project Structure & Navigation
// pubspec.yaml name: my_flutter_app version: 1.0.0
dependencies: flutter: sdk: flutter provider: ^6.0.0 http: ^1.1.0 go_router: ^12.0.0
// main.dart with GoRouter navigation import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart';
void main() { runApp(const MyApp()); }
class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key);
@override Widget build(BuildContext context) { return MaterialApp.router( title: 'Flutter App', theme: ThemeData(primarySwatch: Colors.blue, useMaterial3: true), routerConfig: _router, ); } }
final GoRouter _router = GoRouter( routes: <RouteBase>[ GoRoute( path: '/', builder: (context, state) => const HomeScreen(), routes: [ GoRoute( path: 'details/:id', builder: (context, state) => DetailsScreen( itemId: state.pathParameters['id']! ), ), ], ), GoRoute( path: '/profile', builder: (context, state) => const ProfileScreen(), ), ], );
- State Management with Provider
import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'dart:convert';
class User { final String id; final String name; final String email;
User({required this.id, required this.name, required this.email});
factory User.fromJson(Map<String, dynamic> json) { return User( id: json['id'], name: json['name'], email: json['email'], ); } }
class UserProvider extends ChangeNotifier { User? _user; bool _isLoading = false; String? _error;
User? get user => _user; bool get isLoading => _isLoading; String? get error => _error;
Future<void> fetchUser(String userId) async { _isLoading = true; _error = null; notifyListeners();
try {
final response = await http.get(
Uri.parse('https://api.example.com/users/$userId'),
headers: {'Content-Type': 'application/json'},
);
if (response.statusCode == 200) {
_user = User.fromJson(jsonDecode(response.body));
} else {
_error = 'Failed to fetch user';
}
} catch (e) {
_error = 'Error: ${e.toString()}';
} finally {
_isLoading = false;
notifyListeners();
}
}
void logout() { _user = null; notifyListeners(); } }
class ItemsProvider extends ChangeNotifier { List<Map<String, dynamic>> _items = [];
List<Map<String, dynamic>> get items => _items;
Future<void> fetchItems() async { try { final response = await http.get( Uri.parse('https://api.example.com/items'), );
if (response.statusCode == 200) {
_items = List<Map<String, dynamic>>.from(
jsonDecode(response.body) as List
);
notifyListeners();
}
} catch (e) {
print('Error fetching items: $e');
}
} }
- Screens with Provider Integration
class HomeScreen extends StatefulWidget { const HomeScreen({Key? key}) : super(key: key);
@override State<HomeScreen> createState() => _HomeScreenState(); }
class _HomeScreenState extends State<HomeScreen> { @override void initState() { super.initState(); Future.microtask(() { Provider.of<ItemsProvider>(context, listen: false).fetchItems(); }); }
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Home Feed')), body: Consumer<ItemsProvider>( builder: (context, itemsProvider, child) { if (itemsProvider.items.isEmpty) { return const Center(child: Text('No items found')); } return ListView.builder( itemCount: itemsProvider.items.length, itemBuilder: (context, index) { final item = itemsProvider.items[index]; return ItemCard(item: item); }, ); }, ), ); } }
class ItemCard extends StatelessWidget { final Map<String, dynamic> item;
const ItemCard({required this.item, Key? key}) : super(key: key);
@override Widget build(BuildContext context) { return Card( margin: const EdgeInsets.all(8), child: ListTile( title: Text(item['title'] ?? 'Untitled'), subtitle: Text(item['description'] ?? ''), trailing: const Icon(Icons.arrow_forward), onTap: () => context.go('/details/${item['id']}'), ), ); } }
class DetailsScreen extends StatelessWidget { final String itemId;
const DetailsScreen({required this.itemId, Key? key}) : super(key: key);
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Details')), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text('Item ID: $itemId', style: const TextStyle(fontSize: 18)), const SizedBox(height: 16), ElevatedButton( onPressed: () => context.pop(), child: const Text('Go Back'), ), ], ), ), ); } }
class ProfileScreen extends StatelessWidget { const ProfileScreen({Key? key}) : super(key: key);
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Profile')), body: Consumer<UserProvider>( builder: (context, userProvider, child) { if (userProvider.isLoading) { return const Center(child: CircularProgressIndicator()); } if (userProvider.error != null) { return Center(child: Text('Error: ${userProvider.error}')); } final user = userProvider.user; if (user == null) { return const Center(child: Text('No user data')); } return Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Name: ${user.name}', style: const TextStyle(fontSize: 18)), Text('Email: ${user.email}', style: const TextStyle(fontSize: 16)), const SizedBox(height: 16), ElevatedButton( onPressed: () => userProvider.logout(), child: const Text('Logout'), ), ], ), ); }, ), ); } }
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