My app uses a Firestore database to store all of its data. I present the data in lists throughout my app by using a FutureBuilder to get the data from Firestore. The PROBLEM is that my app gets slower and slower as you use it. I checked the memory usage and it continues to grow at the same rate that the speed of the app slows down. I 'think' this is because of my usage of FutureBuilder. Does ANYONE know of a more efficient way to write this code so that it doesn't leak memory and slow down?
class HomeScreen extends StatefulWidget {
@override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
TextEditingController _searchController = TextEditingController();
rive.Artboard _artboard;
RefreshController _controller;
bool _searchNotification = false;
bool _addNotification = false;
bool _personNotification = false;
bool splitErrorBool = false;
int sum;
bool phoneMode;
@override
void initState() {
_loadRiveFile();
super.initState();
}
/// Loads a Rive file
void _loadRiveFile() async {
final bytes = await rootBundle.load('assets/space_reload.riv');
final file = rive.RiveFile();
if (file.import(bytes)) {
setState(() => _artboard = file.mainArtboard
..addController(_controller = RefreshController()));
}
}
void _newNotification() {
setState(() {
_searchNotification = true;
_addNotification = true;
_personNotification = true;
});
}
@override
void dispose() {
super.dispose();
}
Widget buildRefreshWidget(
BuildContext context,
RefreshIndicatorMode refreshState,
double pulledExtent,
double refreshTriggerPullDistance,
double refreshIndicatorExtent) {
_controller.refreshState = refreshState;
_controller.pulledExtent = pulledExtent;
_controller.triggerThreshold = refreshTriggerPullDistance;
_controller.refreshIndicatorExtent = refreshIndicatorExtent;
return _artboard != null
? rive.Rive(
artboard: _artboard, fit: BoxFit.cover, alignment: Alignment.center)
: Container();
}
@override
Widget build(BuildContext context) {
double x = MediaQuery.of(context).size.width;
double y = MediaQuery.of(context).size.height;
if (x < 600) {
setState(() {
phoneMode = true;
});
} else {
setState(() {
phoneMode = false;
});
}
Location sharedData = Provider.of<Location>(context, listen: false);
List<String> searchList = _searchController.text.toUpperCase().split(' ');
List<String> endSearchList =
(_searchController.text.toUpperCase() + "\uf8ff").split(' ');
return FutureBuilder(
future: Collection<Report>(
path: '${sharedData.location}/data/reports',
searchText: searchList,
endSearchList: endSearchList)
.getName(),
builder: (BuildContext context, AsyncSnapshot snap) {
if (snap.hasData) {
List<Report> reports = snap.data;
sum = reports
.map((expense) => expense.split)
.fold(0, (prev, amount) => prev + amount);
if (sum != 100) {
splitErrorBool = true;
} else {
splitErrorBool = false;
}
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
title: Text(
sharedData.location,
style: TextStyle(
fontFamily: 'Open Sans',
fontWeight: FontWeight.bold,
fontSize: phoneMode ? 23.sp : 20),
),
centerTitle: true,
leading: Padding(
padding: const EdgeInsets.only(left: 15),
child: Image.asset(
'assets/ledger_logo.png',
),
),
actions: [
Visibility(
visible: splitErrorBool,
child: IconButton(
icon: Icon(
FontAwesome5Solid.hand_paper,
color: Colors.red,
size: phoneMode ? 30.sp : null,
),
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return StopDialog();
});
}),
),
IconButton(
icon: Icon(
Icons.search,
size: phoneMode ? 30.sp : null,
),
onPressed: () {
showSearch(context: context, delegate: UserSearch());
setState(() {
_searchNotification = false;
});
}),
IconButton(
icon: Icon(
Icons.add,
size: phoneMode ? 30.sp : null,
),
onPressed: () {
Navigator.pushNamed(context, '/createUser');
setState(() {
_addNotification = false;
});
}),
Padding(
padding: EdgeInsets.only(right: phoneMode ? 10.w : 10),
child: IconButton(
icon: Icon(
Icons.person,
size: phoneMode ? 30.sp : null,
),
onPressed: () {
Navigator.pushNamed(context, '/profile');
setState(() {
_personNotification = false;
});
}),
),
],
),
body: NotificationListener<ScrollNotification>(
onNotification: (notification) {
if (notification is ScrollEndNotification) {
_controller.reset();
}
return true;
},
child: CustomScrollView(
physics: BouncingScrollPhysics(),
slivers: [
CupertinoSliverRefreshControl(
refreshTriggerPullDistance: 240.0,
refreshIndicatorExtent: 240.0,
builder: buildRefreshWidget,
onRefresh: () {
return Future<void>.delayed(const Duration(seconds: 5))
..then<void>((_) {
if (mounted) {
setState(() {});
}
});
},
),
SliverSafeArea(
top: false,
sliver: SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding:
EdgeInsets.only(left: phoneMode ? 5 : 10.0),
child: Column(
children: [
Container(
width: phoneMode ? 160.w : x * .385,
height: phoneMode
? MediaQuery.of(context).size.height -
75.h
: MediaQuery.of(context).size.height -
75,
child: Padding(
padding: const EdgeInsets.only(
bottom: 20, top: 20),
child: ListView(
shrinkWrap: true,
padding:
const EdgeInsets.only(bottom: 10),
primary: false,
children: reports
.map((report) =>
UsersItem(report: report))
.toList(),
),
),
),
],
),
),
Expanded(
flex: 3,
child: Padding(
padding: const EdgeInsets.only(top: 20),
child: Column(
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.spaceEvenly,
crossAxisAlignment:
CrossAxisAlignment.center,
children: [
InkWell(
onTap: () =>
Navigator.pushReplacementNamed(
context, '/pdf'),
child: SizedBox(
width:
phoneMode ? 105.w : x * .25,
height:
phoneMode ? 110.h : x * .23,
child: Container(
decoration: BoxDecoration(
border: Border.all(
width: phoneMode ? 4 : 5,
color: Colors.blue
.withOpacity(.5)),
borderRadius:
BorderRadius.circular(
15.0),
gradient: RadialGradient(
colors: [
const Color(0xFF5e5e5e),
const Color(0xFF424242),
],
// stops: [0.0, 0.1],
),
),
clipBehavior: Clip.antiAlias,
child: FittedBox(
fit: BoxFit.contain,
child: Padding(
padding:
const EdgeInsets.all(
7.0),
child: Column(
mainAxisAlignment:
MainAxisAlignment
.spaceEvenly,
children: [
Icon(
FlutterIcons
.file_pdf_mco,
color: Colors.blue,
size: phoneMode
? 50.w
: x * .14,
),
Text(
'Pdf',
style: GoogleFonts
.poppins(
fontSize:
phoneMode
? 15
.sp
: 20,
fontWeight:
FontWeight
.w400),
),
Visibility(
visible: true,
child: Text(
'Documents',
style: TextStyle(
fontSize:
phoneMode
? 11.sp
: 15,
fontWeight:
FontWeight
.w300),
),
),
],
),
),
),
),
),
),
InkWell(
onTap: () =>
Navigator.pushReplacementNamed(
context, '/reports'),
child: SizedBox(
width:
phoneMode ? 105.w : x * .25,
height:
phoneMode ? 110.h : x * .23,
child: Container(
decoration: BoxDecoration(
border: Border.all(
width: phoneMode ? 4 : 5,
color: Colors.green
.withOpacity(.5)),
borderRadius:
BorderRadius.circular(
15.0),
gradient: RadialGradient(
colors: [
const Color(0xFF5e5e5e),
const Color(0xFF424242),
],
// stops: [0.0, 0.1],
),
),
clipBehavior: Clip.antiAlias,
child: FittedBox(
fit: BoxFit.contain,
child: Padding(
padding:
const EdgeInsets.all(
7.0),
child: Column(
mainAxisAlignment:
MainAxisAlignment
.spaceEvenly,
children: [
Icon(
FlutterIcons
.chart_bubble_mco,
color: Colors.green,
size: phoneMode
? 50.w
: x * .14,
),
Text(
'Reports',
style: GoogleFonts
.poppins(
fontSize:
phoneMode
? 15
.sp
: 20,
fontWeight:
FontWeight
.w400),
),
Visibility(
visible: true,
child: Text(
'Datasheet',
style: GoogleFonts.poppins(
fontSize:
phoneMode
? 11.sp
: 15,
fontWeight:
FontWeight
.w300),
),
),
],
),
),
),
),
),
),
],
),
SizedBox(
height: 25.h,
),
Padding(
padding: EdgeInsets.only(
left: phoneMode ? 5.w : 10,
right: phoneMode ? 5.w : 10),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(
phoneMode ? 20.w : 30.0),
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
const Color(0xFF424242),
const Color(0xFF424242),
],
// stops: [0.0, 0.1],
),
),
child: Column(
children: [
Padding(
padding: EdgeInsets.only(
top: phoneMode ? 10.h : 20,
left: phoneMode ? 5.w : 5,
),
child: Row(children: [
SizedBox(
width:
phoneMode ? 5.w : 5),
Icon(
Icons.history,
color: Colors.white
.withOpacity(.8),
size: phoneMode ? 20.w : 25,
),
SizedBox(width: 5),
Text('Transaction History',
style: TextStyle(
fontSize: phoneMode
? 13.sp
: 20,
color: Colors.white
.withOpacity(
.8))),
]),
),
SizedBox(
height: phoneMode
? 510.h
: MediaQuery.of(context)
.size
.height *
.64,
child: Padding(
padding: EdgeInsets.only(
top: phoneMode
? 15.h
: 20,
left: 0,
right: 0,
bottom: phoneMode
? 10.h
: 30),
child:
SingleChildScrollView(
child: Column(
children: [
FutureBuilder(
future: Collection<
History>(
path:
'${sharedData.location}/data/history')
.getHistory(),
builder:
(BuildContext
context,
AsyncSnapshot
snap) {
if (snap
.hasData) {
List<History>
historyList =
snap.data;
return ListView(
shrinkWrap:
true,
primary:
false,
children:
historyList
.map((history) =>
HistoryItem(
history: history,
))
.toList(),
);
} else {
return Loader();
}
},
),
],
),
),
)),
],
),
),
),
],
),
),
),
],
);
}, childCount: 1),
),
),
],
),
),
);
} else {
return LoadingScreen();
}
});
}
}
确保anyController.dispose()
任何控制器,如void dispose() {}
中的 RefreshController 和 TextEditingController 以避免内存泄漏。
One problem with your code is that it calls setState()
within the build()
method. This is a problem because setState()
will trigger a Widget rebuild, hence build()
will be called again, which will in turn call setState()
again, etc.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.