Flutter优化商家营业时间展示:改用FutureBuilder方案求助
优化Flutter商家营业时间展示(使用FutureBuilder)
我已经完成了以下Flutter代码的编写和编译,但知道可以用更优的方式展示商家营业时间。我打算用FutureBuilder实现,但缺乏相关经验。当前代码会先加载商家信息,再异步获取营业时间,存在性能问题,希望能优化实现首次加载就展示营业时间。
原代码
import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:intl/intl.dart'; import 'package:rapideats/global/global.dart'; import '../mainScreens/menus_screen.dart'; import '../models/sellers.dart'; class SellersDesignWidget extends StatefulWidget { Sellers? model; BuildContext? context; SellersDesignWidget({this.model,this.context}); String status = ""; @override State<SellersDesignWidget> createState() => _SellersDesignWidgetState(); } class _SellersDesignWidgetState extends State<SellersDesignWidget> { var status = "Closed"; getOpeningHours() async { var date = DateTime.now(); var from = ""; var to = ""; var TodaysDate = DateFormat('EEEE').format(date).substring(0,3).toLowerCase(); // prints mon,tue etc. QuerySnapshot oh = await FirebaseFirestore.instance.collection("openingHours") .where("sellerUID", isEqualTo:widget.model!.sellerUID!) .get(); if(oh.docs.isNotEmpty) { for (int i=0; i < oh.docs.length; i++) { from = oh.docs[i][TodaysDate+"From"].toString(); to = oh.docs[i][TodaysDate+"To"].toString(); } if(from == "00:00" && to == "00:00") { setState(() { status = "Closed"; } ); }else { setState(() { status = "Open Now: " + TodaysDate.toTitleCase() + " " + from + " - " + to + "Hrs"; } ); } } } void initState(){ super.initState(); getOpeningHours(); } @override Widget build(BuildContext context) { return SingleChildScrollView( child : Padding( padding: const EdgeInsets.all(6.0), child: Row( children: [ ClipRRect( borderRadius: BorderRadius.circular(6.0), child: Image.network(widget.model!.sellerAvatarUrl!, height: 150, width: 150, fit:BoxFit.cover, ), ), const SizedBox(width: 10.0,), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox( height: 20, ), Row( mainAxisSize: MainAxisSize.max, children: [ Expanded( child: Text( widget.model!.sellerName!.toTitleCase(), style: const TextStyle( color:Colors.black, fontSize: 24, fontFamily:"Acme", ), ), ), const SizedBox( width: 10, ), ], ), const SizedBox( height: 20, ), Row( children: [ Text( status, style: TextStyle( color: Colors.grey, fontSize: 14, ), ), ], ), const SizedBox( height: 20, ), Row( children: [ Padding( padding: const EdgeInsets.all(4.0), child: status == "Closed" ? const Text( "", ) : ElevatedButton( child: const Text("Order Now"), style: ElevatedButton.styleFrom( backgroundColor:Colors.blueAccent, ), onPressed: () { Navigator.push(context, MaterialPageRoute(builder:(c)=> MenusScreen(model:widget.model))); }, ) ), ] ), ], ), ), ], ), ), ); } }
优化后的代码(使用FutureBuilder)
import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:intl/intl.dart'; import 'package:rapideats/global/global.dart'; import '../mainScreens/menus_screen.dart'; import '../models/sellers.dart'; class SellersDesignWidget extends StatelessWidget { final Sellers? model; const SellersDesignWidget({Key? key, this.model}) : super(key: key); // 封装营业时间异步获取逻辑 Future<String> _getOpeningHours() async { final date = DateTime.now(); final todayAbbr = DateFormat('EEEE').format(date).substring(0, 3).toLowerCase(); final QuerySnapshot oh = await FirebaseFirestore.instance .collection("openingHours") .where("sellerUID", isEqualTo: model!.sellerUID!) .get(); if (oh.docs.isNotEmpty) { // 假设单个商家对应一条营业时间记录,直接取第一个文档 final doc = oh.docs.first; final from = doc["${todayAbbr}From"].toString(); final to = doc["${todayAbbr}To"].toString(); if (from == "00:00" && to == "00:00") { return "Closed"; } else { return "Open Now: ${todayAbbr.toTitleCase()} $from - $to Hrs"; } } return "Closed"; } @override Widget build(BuildContext context) { return SingleChildScrollView( child: Padding( padding: const EdgeInsets.all(6.0), child: Row( children: [ ClipRRect( borderRadius: BorderRadius.circular(6.0), child: Image.network( model!.sellerAvatarUrl!, height: 150, width: 150, fit: BoxFit.cover, ), ), const SizedBox(width: 10.0), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 20), Text( model!.sellerName!.toTitleCase(), style: const TextStyle( color: Colors.black, fontSize: 24, fontFamily: "Acme", ), ), const SizedBox(height: 20), // 用FutureBuilder管理异步状态 FutureBuilder<String>( future: _getOpeningHours(), initialData: "Loading...", // 初始加载提示 builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return Text( snapshot.data!, style: TextStyle( color: Colors.grey, fontSize: 14, ), ); } else if (snapshot.hasError) { return const Text( "加载营业时间出错", style: TextStyle( color: Colors.red, fontSize: 14, ), ); } else { final status = snapshot.data!; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( status, style: TextStyle( color: Colors.grey, fontSize: 14, ), ), const SizedBox(height: 20), // 仅在营业时显示下单按钮 if (status != "Closed") Padding( padding: const EdgeInsets.all(4.0), child: ElevatedButton( child: const Text("Order Now"), style: ElevatedButton.styleFrom( backgroundColor: Colors.blueAccent, ), onPressed: () { Navigator.push( context, MaterialPageRoute( builder: (c) => MenusScreen(model: model), ), ); }, ), ), ], ); } }, ), ], ), ), ], ), ), ); } }
优化说明
- 改用StatelessWidget:通过FutureBuilder管理异步数据状态,无需维护组件内部状态,避免不必要的重绘
- 封装异步逻辑:将营业时间获取逻辑封装为独立Future方法,让FutureBuilder自动处理加载、完成、错误三种状态
- 添加加载提示:通过
initialData设置加载时的提示文本,提升用户体验 - 错误处理:捕获异步请求异常,显示友好提示
- 简化数据处理:假设单个商家对应一条营业时间记录,直接取第一个文档(若你的数据结构为多条,可按需调整遍历逻辑)
- 避免全局重绘:原代码中
setState会触发整个组件重绘,优化后仅更新营业时间相关的局部UI,性能更优
内容的提问来源于stack exchange,提问作者ivias
相关产品推荐
相关产品推荐

