简体   繁体   中英

Flutter Memory Leak with Firestore Futurebuilder

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM