How to show a Progress Indicator before my listview.builder has finished building the listview

Viewed 620

I have a huge ListView with a lots of data, so it takes a few of seconds before loading it, is there a way to show a CircularProgressIndicator or LinearProgressIndicator? is there a way to understand the state of the ListView.builder?

Below there is the code with what I've tried to do:

class _PlantsPageState extends State<PlantsPage> {
  TextEditingController controller = TextEditingController();
  List<dynamic> _searchResult = [];
  List<dynamic> _plantDetails = [];
  bool _isLoading = false;

  Future getPlantDetails() async {
    final response = await Plants().getPlants();

    setState(() {
      for (dynamic plant in response.data) {
        //print(plant['Name']);
        _plantDetails.add(plant);
      }
    });
  }

  @override
  void initState() {
    super.initState();

    getPlantDetails();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('plants'),
        elevation: 0.0,
      ),
      body: _isLoading
          ? Center(
              child: CircularProgressIndicator(),
            )
          : Column(
              children: <Widget>[
        
                Expanded(
                    child:
                        _searchResult.length != 0 || controller.text.isNotEmpty
                            ? _plantsListView(_searchResult)
                            : _plantsListView(_plantDetails)
                            ),
              ],
            ),

  ListView _plantsListView(_plant) {
    _isLoading=true;
    return ListView.builder(
        itemCount: _plant.length,
        itemBuilder: (context, index) {
          return Container(
              padding: const EdgeInsets.symmetric(horizontal: 20),
              child: Row(children: [
                SingleChildScrollView(
                  child: Column(
                    children: <Widget>[
                      InkWell(
                        child: Row(
                          children: [
                            SizedBox(width: 100),
                            Container(
                                width: 250,
                                child: Column(
                                  crossAxisAlignment: CrossAxisAlignment.start,
                                  children: [
                                    Text(
                                      _plant[index]['Name'],
                                      style: TextStyle(
                                          fontSize: 20.0,
                                          fontWeight: FontWeight.bold),
                                      textAlign: TextAlign.left,
                                    ),
                                    Text(
                                        getCompanies(_plant[index]).toString()),
                                    Text(getClients(_plant[index]).toString()),
                                  ],
                                )),
                            SizedBox(width: 100),
                            Container(
                              width: 150,
                              child: Row(
                                children: isOnline(_plant[index]) +
                                    getTopics(_plant[index]) +
                                    hasAlarm(_plant[index]),
                              ),
                            ),
                            SizedBox(width: 100),
                            Container(
                                width: 150,
                                child: Visibility(
                                    visible: isVisible(_plant[index]),
                                    child: Column(
                                      crossAxisAlignment:
                                          CrossAxisAlignment.start,
                                      children: [
                                        Text('Total'),
                                        SizedBox(height: 5),
                                        Row(
                                          crossAxisAlignment:
                                              CrossAxisAlignment.end,
                                          children: [
                                            Text(
                                              calculateProd(_plant[index])
                                                  .toString()
                                                  .replaceAll('.', ','),
                                              style: TextStyle(
                                                  fontSize: 20,
                                                  fontWeight: FontWeight.bold),
                                            ),
                                            Text(' ' + getUnit(_plant[index]))
                                          ],
                                        )
                                      ],
                                    ))),
                          ],
                        ),
                        onTap: () => {},
                      ),
                      Divider(
                        color: Colors.blue,
                        thickness: 5,
                      )
                    ],
                  ),
                ),
              ]));
        });
  }

How Could I do it?

4 Answers

An excellent approach is to paginate the results of the list. An example can be found in this tutorial. From Ying Chen, Medium

just add this changes to code it will work `

 class _PlantsPageState extends State<PlantsPage> {
  TextEditingController controller = TextEditingController();
  List<dynamic> _searchResult = [];
  List<dynamic> _plantDetails = [];
  bool _isLoading ; // ---------do this here 

  Future getPlantDetails() async {
    final response = await Plants().getPlants();

    setState(() {
      for (dynamic plant in response.data) {
        //print(plant['Name']);
        _plantDetails.add(plant);
        _isLoading = false; // do this false here after getting all data 
      }
    });
  }

  @override
  void initState() {
    super.initState();
     _isLoding = true ;// do this true here 

    getPlantDetails();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('plants'),
        elevation: 0.0,
      ),
      body: _isLoading
          ? Center(
              child: CircularProgressIndicator(),
            )
          : Column(
              children: <Widget>[
        
                Expanded(
                    child:
                        _searchResult.length != 0 || controller.text.isNotEmpty
                            ? _plantsListView(_searchResult)
                            : _plantsListView(_plantDetails)
                            ),
              ],
            ),

  ListView _plantsListView(_plant) {
    // remove _isLoading from here .....
    return ListView.builder(
        itemCount: _plant.length,
        itemBuilder: (context, index) {
          return Container(
              padding: const EdgeInsets.symmetric(horizontal: 20),
              child: Row(children: [
                SingleChildScrollView(
                  child: Column(
                    children: <Widget>[
                      InkWell(
                        child: Row(
                          children: [
                            SizedBox(width: 100),
                            Container(
                                width: 250,
                                child: Column(
                                  crossAxisAlignment: CrossAxisAlignment.start,
                                  children: [
                                    Text(
                                      _plant[index]['Name'],
                                      style: TextStyle(
                                          fontSize: 20.0,
                                          fontWeight: FontWeight.bold),
                                      textAlign: TextAlign.left,
                                    ),
                                    Text(
                                        getCompanies(_plant[index]).toString()),
                                    Text(getClients(_plant[index]).toString()),
                                  ],
                                )),
                            SizedBox(width: 100),
                            Container(
                              width: 150,
                              child: Row(
                                children: isOnline(_plant[index]) +
                                    getTopics(_plant[index]) +
                                    hasAlarm(_plant[index]),
                              ),
                            ),
                            SizedBox(width: 100),
                            Container(
                                width: 150,
                                child: Visibility(
                                    visible: isVisible(_plant[index]),
                                    child: Column(
                                      crossAxisAlignment:
                                          CrossAxisAlignment.start,
                                      children: [
                                        Text('Total'),
                                        SizedBox(height: 5),
                                        Row(
                                          crossAxisAlignment:
                                              CrossAxisAlignment.end,
                                          children: [
                                            Text(
                                              calculateProd(_plant[index])
                                                  .toString()
                                                  .replaceAll('.', ','),
                                              style: TextStyle(
                                                  fontSize: 20,
                                                  fontWeight: FontWeight.bold),
                                            ),
                                            Text(' ' + getUnit(_plant[index]))
                                          ],
                                        )
                                      ],
                                    ))),
                          ],
                        ),
                        onTap: () => {},
                      ),
                      Divider(
                        color: Colors.blue,
                        thickness: 5,
                      )
                    ],
                  ),
                ),
              ]));
        });
  }

`

You can simply achieve this by using the FutureBuilder widget in Flutter by providing initialData and future properties. Find the code snippets below:

  Future<String> _loadData() async {
    // Specifies the indicator's duration / delay
    await Future.delayed(const Duration(seconds: 3)); 
    return Future<String>.value('Loaded');
  }
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
       appBar: AppBar(),
       body: FutureBuilder<String>(
         future: _loadData(),
         initialData: 'Loading',
         builder: (BuildContext context, AsyncSnapshot snapshot) {
           if(snapshot.hasData && snapshot.data == 'Loading') {
              return _buildIndicator();
           } else {
             return _buildListView();
           }
         },
       ),
    );
  }

  Widget _buildListView() {
    return Column(
            children: <Widget>[
              Expanded(
                  child:
                      (_searchResult.length != 0 || controller.text.isNotEmpty)
                          ? _plantsListView(_searchResult)
                          : _plantsListView(_plantDetails)
                          ),
            ],
          );
  }

  Widget _buildIndicator() {
    return Center(
            child: CircularProgressIndicator(),
          );
  }

I think the problem is simply that you go through your list twice unnecessarily. in your response.data you should already have what you need, so you can remove this piece of code

 for (dynamic plant in response.data) {
        //print(plant['Name']);
        _plantDetails.add(plant);
      }

in order to avoid passing it again as it is a huge object as you said and you won't need the progress indicator

Related