Flutter中StatefulWidget类本地方法未执行问题解决
如何让Flutter页面加载时自动执行Firestore数据获取方法?
我开发了一个Flutter类用于展示Firestore数据库中的文档列表,已经编写了_getTrxnData方法来获取文档,但这个方法始终没执行。想知道怎么让它在页面加载时自动执行,同时我不太清楚这类操作的执行流程。相关代码如下:
class CompanyDashboardScreen extends ConsumerStatefulWidget { static const String id = 'user_dashboard_screen'; const CompanyDashboardScreen({super.key}); @override ConsumerState<CompanyDashboardScreen> createState() = _CompanyDashboardScreenState(); } class _CompanyDashboardScreenState extends ConsumerState<CompanyDashboardScreen> { bool showSpinner = false; late List<Map<String, dynamic>> itemsTrxn; bool isLoaded = false; _getTrxnData() async { var collectionTrxn = FirebaseFirestore.instance .collection('company') .doc(ref.read(globalsNotifierProvider).companyId) .collection('trxns'); List<Map<String, dynamic>> tempList = []; var dataTrxn = await collectionTrxn.get(); dataTrxn.docs.forEach((element) { tempList.add(element.data()); }); setState(() { itemsTrxn = tempList; isLoaded = true; }); } @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: CustomAppBar(), drawer: Drawer( child: ListView( children: <Widget>[ ListTile( title: const Text("User Profile"), onTap: () { MainScreen.of(context) ?.setIndex(3); // Added this for BottomNavigationBar sync Navigator.of(context).push(MaterialPageRoute( builder: (context) => const UserProfileScreen())); }, ), ], ), ), body: SafeArea( child: isLoaded ? ListView.builder( itemCount: itemsTrxn.length, itemBuilder: (context, index) { _getTrxnData(); <<<< METHOD HERE return Padding( padding: const EdgeInsets.all(8.0), child: ListTile( isThreeLine: true, title: Row( children: [ Text( 'Client: ${itemsTrxn[index]['clientFName'] ?? 'n/a'} ${itemsTrxn[index]['clientLName'] ?? 'n/a'}', style: const TextStyle( fontWeight: FontWeight.w900, color: Colors.blueAccent), ), ], ), subtitle: Text.rich( TextSpan( text: '${itemsTrxn[index]['propertyAddress'] ?? 'n/a'}, ' '${itemsTrxn[index]['propertyCity'] ?? 'n/a'}, ' '${itemsTrxn[index]['propertyState'] ?? 'n/a'}', children: <TextSpan>[ TextSpan( text: '\nPrice: ${itemsTrxn[index]['contractPrice'] == null ? 'n/a' : NumberFormat.simpleCurrency().format(itemsTrxn[index]['contractPrice'])}\nStatus: ${itemsTrxn[index]['trxnStatus'] ?? 'n/a'}', style: const TextStyle( fontWeight: FontWeight.w900, color: Colors.blueGrey), ) ], ), ), trailing: Text( 'MLS#: ${itemsTrxn[index]['mlsNumber'] ?? 'n/a'}\n${itemsTrxn[index]['clientType']}'), onTap: () { ref .read(globalsNotifierProvider.notifier) .updatenewTrxn(false); ref .read(globalsNotifierProvider.notifier) .updatecurrentTrxnId( itemsTrxn[index]['trxnId']); Navigator.of(context).push(MaterialPageRoute( builder: (context) => TransactionDetailScreen( itemsTrxn[index]['trxnId']))); }, ), ); }, ) : const Text('No Date')), ), ); } }
问题原因
你把_getTrxnData()放在了ListView.builder的itemBuilder里,但初始状态下isLoaded是false,页面会先渲染Text('No Date'),根本不会走到ListView.builder的逻辑里,所以方法完全没机会执行。就算后续isLoaded变成true,每次列表项构建时都会重复调用这个方法,导致多次请求Firestore,这也是不合理的。
解决方案:利用Flutter生命周期方法initState
Flutter的StatefulWidget有一套生命周期流程,其中initState方法会在Widget创建完成、第一次调用build之前执行,非常适合用来做初始化数据加载的操作。
修改步骤:
- 重写
initState方法,在里面调用_getTrxnData - 删除
itemBuilder里的_getTrxnData()调用,避免重复请求
修改后的代码关键部分:
class _CompanyDashboardScreenState extends ConsumerState<CompanyDashboardScreen> { bool showSpinner = false; late List<Map<String, dynamic>> itemsTrxn; bool isLoaded = false; // 重写initState方法 @override void initState() { super.initState(); // 在页面初始化时调用数据获取方法 _getTrxnData(); } _getTrxnData() async { var collectionTrxn = FirebaseFirestore.instance .collection('company') .doc(ref.read(globalsNotifierProvider).companyId) .collection('trxns'); List<Map<String, dynamic>> tempList = []; var dataTrxn = await collectionTrxn.get(); dataTrxn.docs.forEach((element) { tempList.add(element.data()); }); setState(() { itemsTrxn = tempList; isLoaded = true; }); } @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( // ... 其余代码不变 body: SafeArea( child: isLoaded ? ListView.builder( itemCount: itemsTrxn.length, itemBuilder: (context, index) { // 删掉这里的_getTrxnData()调用 return Padding( // ... 列表项代码不变 ); }, ) : const Text('No Date')), ), ); } }
执行流程说明
- 页面创建,
_CompanyDashboardScreenState初始化 initState方法执行,调用_getTrxnData开始从Firestore获取数据- 数据获取完成后,调用
setState更新isLoaded为true,并赋值itemsTrxn setState触发build方法重新执行,此时isLoaded为true,渲染ListView.builder展示数据
内容的提问来源于stack exchange,提问作者LostTexan
相关产品推荐
相关产品推荐

