RepaintBoundary
artificially pretends that the child needs its own composited layer which means the effects in sub-tree are then contained. This is a great tip to improve performance in Flutter boosting to 60 FPS.
check in action: https://codepen.io/mhadaily/pen/ZEWWEKp
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() {
// debugRepaintRainbowEnabled = true;
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
themeMode: ThemeMode.dark,
darkTheme: ThemeData.dark(),
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter RepaintBountery Demo'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
int _counter = 0;
bool isRepaintBoundary;
@override
void initState() {
isRepaintBoundary = false;
super.initState();
}
@override
Widget build(BuildContext context) {
String prefix = isRepaintBoundary ? "With" : "Without";
return Scaffold(
body: Stack(
fit: StackFit.expand,
children: [
isRepaintBoundary
? RepaintBoundary(
child: CustomPaint(
painter: ExpensiveBackground(),
),
)
: CustomPaint(
painter: ExpensiveBackground(),
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'$_counter',
style: TextStyle(fontSize: 40),
),
Padding(
padding: EdgeInsets.all(10),
child: Text(
'$prefix RepaintBoundry',
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
),
SizedBox(
height: 50,
),
OutlineButton(
onPressed: () {
setState(() {
isRepaintBoundary = !isRepaintBoundary;
});
},
child: Text(
'Toggle RepaintBoundry',
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
),
],
),
],
),
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.red,
onPressed: () {
setState(() {
_counter++;
});
},
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
@override
void dispose() {
super.dispose();
}
}
class ExpensiveBackground extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
double width = size.width;
double height = size.height;
final starCount = (width + height) / 8;
for (int i = 0; i < starCount; i++) {
final x = Random().nextInt(width.toInt()).toDouble();
final y = Random().nextInt(height.toInt()).toDouble();
canvas.drawCircle(
Offset(x, y),
2,
Paint()..color = Colors.primaries[i % Colors.primaries.length].withOpacity(0.5),
);
}
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => false;
}