we have a performance problem in our calendar widget. We want to display 3 Years in a scroll view.
Each day should be displayed and some days should be marked. The problem is, that the rendering time of this widget is on some devises up to 5 seconds. I think the problem is that we need over 1000 Text widgets to display it. Does someone have an idea how to improve it?
I have written a small sample app. There are many simplifications in it, like every month has 31 days and the layout is bad, but it shows what we want and that it is too slow.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool year = false;
@override
Widget build(BuildContext context) {
const Key currentYearKey = Key('currentYearKey');
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () => setState(() {
year = !year;
})),
appBar: AppBar(
title: Text(widget.title),
),
body: year
? CustomScrollView(
center: currentYearKey,
slivers: [
SliverToBoxAdapter(
child: Column(
children: [Year(), Year()],
),
),
SliverToBoxAdapter(
child: Year(),
key: currentYearKey,
)
],
)
: Text("1"));
}
}
class Year extends StatelessWidget {
const Year({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Year XX"),
...List.generate(
4,
(rowIndex) => Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: List.generate(
3,
(columnIndex) {
return Expanded(
child: Month(
daySize: 14,
markedDays: [1, 3, 26, 30],
));
},
),
),
),
]);
}
}
class Month extends StatelessWidget {
const Month({required this.markedDays, required this.daySize, Key? key})
: super(key: key);
final List<int> markedDays;
final double daySize;
@override
Widget build(BuildContext context) {
return GridView.count(
padding: EdgeInsets.zero,
crossAxisCount: 7,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
children: List.generate(31, (index) {
final bool isMarked = markedDays.contains(index);
return Center(
child: Container(
height: daySize,
width: daySize,
decoration:
isMarked ? BoxDecoration(color: Colors.lightBlue) : null,
child: Text(
"$index",
style: isMarked ? TextStyle(fontWeight: FontWeight.bold) : null,
),
),
);
}));
}
}
We tried to make as much as possible const, it improved it abit, and make it around 30% faster, but we need it much faster.
We also tried to replace the GridView in the month through a table or row/column construct, but it does not help.
3
Answers
The solution was to compile it in release mode. It does still need some time to render, but much less. Thanks @rszf for the help.
For those who have the same problem, and don't know the build modes, here is some information: https://docs.flutter.dev/testing/build-modes
Instead of
columns
useListView.builder
, this helped me with long lists:https://docs.flutter.dev/cookbook/lists/long-lists
In this case you defiantly must use GridView.builder / ListView.builder.
This will lead to build widgets on demand. Instead of building everything at once which is Bad practice.