You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 12:45:41