skip to Main Content

I have managed to compile the code below but know that I can display the opening hours using a better method. I believe it can be done using Future Builder but I am not experienced as yet. The code below loads the items and then goes and find out the opening hours. Can anyone assist to optimize this code so It shows on first instance. I believe there are performance issues with the code below.

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)));
                        },
                         )
                        ),
                      ]
                  ),
                ],
              ),
            ),
          ],
        ),
        ),
    );
  }
}

2

Answers


  1. I recommend using a state management package to handle your UI states in an easier and cleaner way.
    As an example you can use Bloc or Provider (There’s more packages)

    But as an answer for your question, and to handle your state using FutureBuilder to optimize your UI Performance you can use this code

    
    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';
    
    /// We Changed it to StatelessWidget as we don't need to rebuild the whole screen again
    class SellersDesignWidget extends StatelessWidget {
    
      final Sellers? model;
    
      SellersDesignWidget({this.model});
    
      /// We will use the same function with some modification to get the data and return the value to the FutureBuilder
      Future<String> getOpeningHours() async
      {
        String status = "Closed";
        DateTime date = DateTime.now();
        String from = "";
        String to = "";
        String todaysDate = DateFormat('EEEE').format(date).substring(0, 3).toLowerCase();
        // prints mon,tue etc.
    
        QuerySnapshot oh = await FirebaseFirestore.instance.collection("openingHours")
            .where("sellerUID", isEqualTo: 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") {
            status = "Closed";
          } else {
            status = "Open Now: " + todaysDate.toTitleCase() + " " + from + " - " + to + "Hrs";
          }
        }
        return status;
      }
    
      @override
      Widget build(BuildContext context) {
        /// As most of your UI doesn't depend on the data from Firebase so it can be shown directly
        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,
                      ),
                      Row(
                        mainAxisSize: MainAxisSize.max,
                        children: [
                          Expanded(
                            child: Text(
                              model!.sellerName!.toTitleCase(),
                              style: const TextStyle(
                                color: Colors.black,
                                fontSize: 24,
                                fontFamily: "Acme",
                              ),
                            ),
                          ),
                          const SizedBox(
                            width: 10,
                          ),
                        ],
                      ),
                      const SizedBox(
                        height: 20,
                      ),
                      /// For the part needs the remote data we wrap it in future builder
                      FutureBuilder(
                          future: getOpeningHours(),
                          builder: (ctx, AsyncSnapshot<String?> status) {
                            /// When data is loading(status == null) we display a progress indicator and prevent the user from performing any actions
                            return Column(
                              crossAxisAlignment: CrossAxisAlignment.start,
                              mainAxisAlignment: MainAxisAlignment.start,
                              mainAxisSize: MainAxisSize.min,
                              children: [
                                Row(
                                  children: [
                                    status.data == null ?
                                    //Change SizedBox Size if needed
                                    SizedBox(
                                      width: 30, child: CircularProgressIndicator(),)
                                        : Text(
                                      status.data!,
                                      style: TextStyle(
                                        color: Colors.grey,
                                        fontSize: 14,
                                      ),
                                    ),
                                  ],
                                ),
                                const SizedBox(
                                  height: 20,
                                ),
                                Row(
                                    children: [
                                      Padding(
                                          padding: const EdgeInsets.all(4.0),
                                          child:
                                          status.data == "Closed"
                                              ? const Text(
                                            "",
                                          )
                                              : ElevatedButton(
                                            child: status.data == null ?
                                            //Change SizedBox Size if needed
                                            SizedBox(
                                              width: 30, child: CircularProgressIndicator(),) : Text("Order Now"),
                                            style: ElevatedButton.styleFrom(
                                              backgroundColor: Colors.blueAccent,
                                            ),
                                            onPressed: () {
                                              if (status.data != null) {
                                                Navigator.push(context,
                                                    MaterialPageRoute(builder: (c) => MenusScreen(model: model)));
                                              }
                                            },
                                          )
                                      ),
                                    ]
                                ),
                              ],
                            );
                          }),
                    ],
                  ),
                ),
              ],
            ),
          ),
        );
      }
    }
    
    
    Login or Signup to reply.
  2. The cleanest way is:
    1 – You show the first paint and give a condition to the opening hours block. If hours have been fetched, display them, else, show a progress indicator or a loading text or any kind of indication to the user that something is being loaded.
    2- Then, in the init state, don’t forget to set haveFetched to false when you call the function, and to true when it finishes, you call the fetch function. You need to handle possible errors as well by giving it some sort of default times or ‘error’ text.
    As Hesham Erfan mentioned, if you have a UI heavy design, you should use a state management solution and stateless widgets since it will improve performance. I recommend:
    1 – getx for small projects (fast to code, also includes navigator, context-less toasts, snackbars, screen width and height and more!),
    2 – provider all around good state manager (very popular good docs, also easy to learn), and
    3 – bloc for corporate (it has a lot of boiler plate but a very similar structure all around). You would have to move the init state logic to the state managing solution you implement.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search