Flutter Performance Optimization
•14 min read
This guide covers techniques and best practices for optimizing Flutter application performance.
1. Widget Optimization
Const Constructors
// Good class CustomButton extends StatelessWidget { const CustomButton({ Key? key, required this.text, required this.onPressed, }) : super(key: key); final String text; final VoidCallback onPressed; @override Widget build(BuildContext context) { return ElevatedButton( onPressed: onPressed, child: Text(text), ); } } // Bad class CustomButton extends StatelessWidget { CustomButton({ Key? key, required this.text, required this.onPressed, }) : super(key: key); final String text; final VoidCallback onPressed; @override Widget build(BuildContext context) { return ElevatedButton( onPressed: onPressed, child: Text(text), ); } }
Repaint Boundaries
class AnimatedWidget extends StatefulWidget { @override _AnimatedWidgetState createState() => _AnimatedWidgetState(); } class _AnimatedWidgetState extends State<AnimatedWidget> with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation<double> _animation; @override void initState() { super.initState(); _controller = AnimationController( vsync: this, duration: const Duration(seconds: 1), ); _animation = Tween<double>(begin: 0, end: 1).animate(_controller); _controller.repeat(); } @override Widget build(BuildContext context) { return RepaintBoundary( child: AnimatedBuilder( animation: _animation, builder: (context, child) { return Transform.rotate( angle: _animation.value * 2 * pi, child: child, ); }, child: const FlutterLogo(size: 100), ), ); } @override void dispose() { _controller.dispose(); super.dispose(); } }
Selective Rebuilds
class CounterWidget extends StatelessWidget { const CounterWidget({ Key? key, required this.count, required this.onIncrement, }) : super(key: key); final int count; final VoidCallback onIncrement; @override Widget build(BuildContext context) { return Column( children: [ Text('Count: $count'), ElevatedButton( onPressed: onIncrement, child: const Text('Increment'), ), ], ); } } // Usage class ParentWidget extends StatefulWidget { @override _ParentWidgetState createState() => _ParentWidgetState(); } class _ParentWidgetState extends State<ParentWidget> { int _count = 0; String _title = 'Counter App'; void _increment() { setState(() { _count++; }); } @override Widget build(BuildContext context) { return Column( children: [ Text(_title), CounterWidget( count: _count, onIncrement: _increment, ), ], ); } }
2. State Management Optimization
Efficient State Updates
class CounterProvider extends ChangeNotifier { int _count = 0; int get count => _count; void increment() { _count++; notifyListeners(); } } // Usage class CounterWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Consumer<CounterProvider>( builder: (context, provider, _) { return Column( children: [ Text('Count: ${provider.count}'), ElevatedButton( onPressed: provider.increment, child: const Text('Increment'), ), ], ); }, ); } }
State Persistence
class AppState extends ChangeNotifier { final SharedPreferences _prefs; int _count = 0; AppState(this._prefs) { _loadState(); } int get count => _count; void increment() { _count++; _saveState(); notifyListeners(); } Future<void> _loadState() async { _count = _prefs.getInt('count') ?? 0; notifyListeners(); } Future<void> _saveState() async { await _prefs.setInt('count', _count); } } // Usage void main() async { WidgetsFlutterBinding.ensureInitialized(); final prefs = await SharedPreferences.getInstance(); runApp( ChangeNotifierProvider( create: (_) => AppState(prefs), child: const MyApp(), ), ); }
3. Memory Management
Image Optimization
class OptimizedImage extends StatelessWidget { final String imageUrl; final double? width; final double? height; final BoxFit fit; const OptimizedImage({ Key? key, required this.imageUrl, this.width, this.height, this.fit = BoxFit.cover, }) : super(key: key); @override Widget build(BuildContext context) { return Image.network( imageUrl, width: width, height: height, fit: fit, cacheWidth: width?.toInt(), cacheHeight: height?.toInt(), loadingBuilder: (context, child, loadingProgress) { if (loadingProgress == null) return child; return Center( child: CircularProgressIndicator( value: loadingProgress.expectedTotalBytes != null ? loadingProgress.cumulativeBytesLoaded / loadingProgress.expectedTotalBytes! : null, ), ); }, ); } }
Resource Cleanup
class ResourceManager extends StatefulWidget { final Widget child; const ResourceManager({Key? key, required this.child}) : super(key: key); @override _ResourceManagerState createState() => _ResourceManagerState(); } class _ResourceManagerState extends State<ResourceManager> { final List<StreamSubscription> _subscriptions = []; final List<AnimationController> _controllers = []; void addSubscription(StreamSubscription subscription) { _subscriptions.add(subscription); } void addController(AnimationController controller) { _controllers.add(controller); } @override void dispose() { for (var subscription in _subscriptions) { subscription.cancel(); } for (var controller in _controllers) { controller.dispose(); } super.dispose(); } @override Widget build(BuildContext context) { return widget.child; } }
4. Layout Optimization
Efficient Layouts
class OptimizedLayout extends StatelessWidget { @override Widget build(BuildContext context) { return CustomScrollView( slivers: [ SliverAppBar( title: const Text('Optimized Layout'), floating: true, ), SliverList( delegate: SliverChildBuilderDelegate( (context, index) { return ListTile( title: Text('Item $index'), ); }, childCount: 1000, ), ), ], ); } }
Layout Constraints
class ConstrainedLayout extends StatelessWidget { @override Widget build(BuildContext context) { return LayoutBuilder( builder: (context, constraints) { return SingleChildScrollView( child: ConstrainedBox( constraints: BoxConstraints( minHeight: constraints.maxHeight, ), child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Header Container( height: 100, color: Colors.blue, ), // Content Expanded( child: Container( color: Colors.white, ), ), // Footer Container( height: 100, color: Colors.blue, ), ], ), ), ); }, ); } }
5. Animation Optimization
Efficient Animations
class OptimizedAnimation extends StatefulWidget { @override _OptimizedAnimationState createState() => _OptimizedAnimationState(); } class _OptimizedAnimationState extends State<OptimizedAnimation> with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation<double> _animation; @override void initState() { super.initState(); _controller = AnimationController( vsync: this, duration: const Duration(seconds: 1), ); _animation = CurvedAnimation( parent: _controller, curve: Curves.easeInOut, ); _controller.repeat(); } @override Widget build(BuildContext context) { return AnimatedBuilder( animation: _animation, builder: (context, child) { return Transform.scale( scale: 1.0 + (_animation.value * 0.2), child: child, ); }, child: const FlutterLogo(size: 100), ); } @override void dispose() { _controller.dispose(); super.dispose(); } }
Animation Performance
class PerformanceAnimation extends StatefulWidget { @override _PerformanceAnimationState createState() => _PerformanceAnimationState(); } class _PerformanceAnimationState extends State<PerformanceAnimation> with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation<double> _animation; @override void initState() { super.initState(); _controller = AnimationController( vsync: this, duration: const Duration(seconds: 1), ); _animation = Tween<double>(begin: 0, end: 1).animate(_controller); _controller.repeat(); } @override Widget build(BuildContext context) { return RepaintBoundary( child: AnimatedBuilder( animation: _animation, builder: (context, child) { return CustomPaint( painter: CirclePainter(_animation.value), child: child, ); }, child: const SizedBox(width: 100, height: 100), ), ); } @override void dispose() { _controller.dispose(); super.dispose(); } } class CirclePainter extends CustomPainter { final double progress; CirclePainter(this.progress); @override void paint(Canvas canvas, Size size) { final paint = Paint() ..color = Colors.blue ..style = PaintingStyle.stroke ..strokeWidth = 2; final center = Offset(size.width / 2, size.height / 2); final radius = size.width / 2 * progress; canvas.drawCircle(center, radius, paint); } @override bool shouldRepaint(CirclePainter oldDelegate) { return progress != oldDelegate.progress; } }
Performance Tools
Flutter DevTools
void main() { // Enable performance overlay debugPaintSizeEnabled = true; debugPaintBaselinesEnabled = true; debugPaintPointersEnabled = true; debugPaintLayerBordersEnabled = true; debugRepaintRainbowEnabled = true; runApp(const MyApp()); }
Dart Observatory
void main() { // Enable observatory debugPrint('Observatory URL: ${Observatory.defaultUri}'); runApp(const MyApp()); }
Best Practices
-
Widget Optimization
- Use const constructors
- Implement repaint boundaries
- Minimize rebuilds
- Use proper widget types
-
State Management
- Choose efficient patterns
- Minimize state updates
- Use proper persistence
- Clean up resources
-
Memory Management
- Optimize images
- Clean up resources
- Use proper caching
- Monitor memory usage
-
Layout Optimization
- Use efficient layouts
- Follow constraints
- Minimize nesting
- Use proper widgets
-
Animation Optimization
- Use efficient animations
- Monitor performance
- Clean up controllers
- Use proper curves
Conclusion
Remember these key points:
- Optimize widgets
- Manage state efficiently
- Handle memory properly
- Optimize layouts
- Use efficient animations
By following these practices, you can:
- Improve app performance
- Reduce memory usage
- Enhance user experience
- Optimize resource usage
Keep optimizing your Flutter applications!