top of page

Lazy load with Sliverlist and CircularProgressIndicator in flutter.

Writer's picture: Programmer's HelpdeskProgrammer's Helpdesk

Updated: May 17, 2020






In this project we will read Employee record from JSON file stored in assets folder and show in list with employee name and his department. When user reach at last item, will show CircularProgressIndicator at bottom of list and we will again read same JSON file and add data in current list. Here, we will use sliver list.


The following are main steps :-


  • Create EmployeeList StatefulWidget class

  • Create SliverList and list item

  • Create Employee model class.

  • Read data from JSON file in assets and set to list.


Create project in Visual Studio by ctrl+shift+p, enter project name lazy_list and hit enter.

It will take some time to create your project structure.


main.dart


void main(List<String> args) {
  runApp(EmployeeList());
}

Create a EmployeeList StatefulWidget class and override initState().



class EmployeeList extends StatefulWidget {
 @override
 State<StatefulWidget> createState() {
     return EmployeeState();
  }
}

class EmployeeState extends State<EmployeeList> {
 List<Employee> _employeeList = [];
 bool _isLoading;
 bool _hasMore = true;
 ScrollController _scrollController = ScrollController();

  @override
 void initState() {
   super.initState();
    _isLoading = true;
    _scrollController.addListener(_scrollListener);
    Future.delayed(Duration(milliseconds: 3), () {
      _getEmployeeList();
    });
 }

 @override
  Widget build(BuildContext context) {
  return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        appBar: AppBar(
          title: Text("Lazy Load"),
        ),
        body: _buildListView(),
      ),
    );
  }
}

_scrollListener method used to determine whether the scrolling is close to bottom.

If list reached at bottom, we again read data from JSON and add to list.


  
_scrollListener() {
 if (_scrollController.offset >=
            _scrollController.position.maxScrollExtent &&
        !_scrollController.position.outOfRange &&
        !_isLoading) {
      _getEmployeeList();
    }
  }
  




Now lets create list. Here we are using sliverlist, becuase of SliverToBoxAdapter it's eazy to show loader at list bottom.



Widget _buildListView() {
 if (_isLoading && _employeeList.length == 0) {
 return _loadingPopup();
 }
 return Padding(
      padding: const EdgeInsets.all(8),
      child: _employeeList.length == 0
          ? Center(
              child: Text("No record found."),
            )
          : CustomScrollView(
              controller: _scrollController,
              slivers: <Widget>[
                SliverList(
                  delegate: SliverChildBuilderDelegate(
                    (context, index) {
                      return _listItem(_employeeList[index]);
                    },
                    childCount: (_employeeList.length),
                    addAutomaticKeepAlives: false,
                  ),
                ),
                SliverToBoxAdapter(
                  child: _isLoading ? _loadingPopup() : SizedBox(),
                ),
              ],
            ),
    );
  }

Lets create list item. Here we show Employee name with his department.



  Widget _listItem(Employee _detail) {
   return Padding(
      padding: const EdgeInsets.all(16.0),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        mainAxisAlignment: MainAxisAlignment.end,
        mainAxisSize: MainAxisSize.max,
        children: <Widget>[
          Text(
            _detail.empName,
            style: TextStyle(fontSize: 14, color: Colors.black),
          ),
          SizedBox(
            height: 2,
          ),
          Text(
            _detail.empDepartment,
            style: TextStyle(fontSize: 12, color: Colors.grey),
          ),
        ],
      ),
    );
  }

And _loadingPopup returns CircularProgressIndicator, to show while reading data.



Widget _loadingPopup() {
 return Container(
      margin: EdgeInsets.only(top: 24, bottom: 16),
      alignment: Alignment.center,
      child: CircularProgressIndicator(
        backgroundColor: Colors.grey,
        valueColor: AlwaysStoppedAnimation<Color>(
          Colors.blue,
        ),
      ),
    );
  }
    

Until now, we have create EmployeeList StatefulWidget, having sliverlist with listitem. Let set data to this list. For that we parse data from JSON file. First create model class Employee.dart to hold parsed data.



class Employee {
  String empName;
  String empDepartment;

  Employee.fromJson(Map<String, dynamic> json) {
    empName = json["name"];
    empDepartment = json["department"];
  }
}

And create employee.json file at assets/resources/json


[
    {
        "id": 1,
        "name": "Employee 1",
        "department": "Production"
    },
    {
        "id": 2,
        "name": "Employee 2",
        "department": "Engineering"
    },
    {
        "id": 3,
        "name": "Employee 3",
        "department": "Designing"
    },
    {
        "id": 4,
        "name": "Employee 4",
        "department": "Store"
    },
    {
        "id": 5,
        "name": "Employee 5",
        "department": "Management"
    },
    {
        "id": 6,
        "name": "Employee 6",
        "department": "Human Resource"
    },
    {
        "id": 7,
        "name": "Employee 7",
        "department": "Information Technology"
    },
    {
        "id": 8,
        "name": "Employee 8",
        "department": "Marketing"
    },
    {
        "id": 9,
        "name": "Employee 9",
        "department": "Sales"
    },
    {
        "id": 10,
        "name": "Employee 10",
        "department": "Quality Assurance"
    },
    {
        "id": 11,
        "name": "Employee 11",
        "department": "Security"
    },
    {
        "id": 12,
        "name": "Employee 12",
        "department": "Account"
    }
]




Create _getEmployeeList function to read and parsed JSON data.



 void _getEmployeeList() async {
    if (!_hasMore) return;
    setState(() {
      _isLoading = true;
    });
    await new Future.delayed(new Duration(seconds: 2));
    String jsonString = await _loadEmployeeData();
    var jsonResult = json.decode(jsonString);
    if (jsonResult != null) {
      var list =
          jsonResult.map<Employee>((json) =>    Employee.fromJson(json)).toList();
      _employeeList.addAll(list);
      _isLoading = false;
    } else {
      _hasMore = false;
    }
    if (mounted) setState(() {});
  }

  Future<String> _loadEmployeeData() async {
    return await rootBundle.loadString('assets/resources/json/employee.json');
  }
 



To show divider in listitem, change in sliverlist code


 
SliverList(delegate: SliverChildBuilderDelegate(
   (context, index) {
     if (index.isOdd) {
           return Divider(color: Colors.blueGrey);
                      }
     return _listItem(_employeeList[index ~/ 2]);
                    },
                    childCount: (_employeeList.length * 2) - 1,
                    addAutomaticKeepAlives: false,
                  ),
                ),

The final code



import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:lazyload/model/employee.dart';

void main(List<String> args) {
  runApp(EmployeeList());
}

class EmployeeList extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return EmployeeState();
  }
}

class EmployeeState extends State<EmployeeList> {
  List<Employee> _employeeList = [];
  bool _isLoading = true;
  bool _hasMore = true;
  ScrollController _scrollController = ScrollController();

  @override
  void initState() {
    super.initState();
    _isLoading = true;
    _scrollController.addListener(_scrollListener);
    Future.delayed(Duration(milliseconds: 3), () {
      _getEmployeeList();
    });
  }

  _scrollListener() {
    if (_scrollController.offset >=
            _scrollController.position.maxScrollExtent &&
        !_scrollController.position.outOfRange &&
        !_isLoading) {
      _getEmployeeList();
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        appBar: AppBar(
          title: Text("Lazy Load"),
        ),
        body: _buildListView(),
      ),
    );
  }

  Widget _buildListView() {
    if (_isLoading && _employeeList.length == 0) {
      return _loadingPopup();
    }
    return Padding(
      padding: const EdgeInsets.all(8),
      child: _employeeList.length == 0
          ? Center(
              child: Text("No record found."),
            )
          : CustomScrollView(
              controller: _scrollController,
              slivers: <Widget>[
                SliverList(
                  delegate: SliverChildBuilderDelegate(
                    (context, index) {
                      if (index.isOdd) {
                        return Divider(color: Colors.blueGrey);
                      }
                      return _listItem(_employeeList[index ~/ 2]);
                    },
                    childCount: (_employeeList.length * 2) - 1,
                    addAutomaticKeepAlives: false,
                  ),
                ),
                SliverToBoxAdapter(
                  child: _isLoading ? _loadingPopup() : SizedBox(),
                ),
              ],
            ),
    );
  }

  Widget _listItem(Employee _detail) {
    return Padding(
      padding: const EdgeInsets.all(16.0),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        mainAxisAlignment: MainAxisAlignment.end,
        mainAxisSize: MainAxisSize.max,
        children: <Widget>[
          Text(
            _detail.empName,
            style: TextStyle(fontSize: 14, color: Colors.black),
          ),
          SizedBox(
            height: 2,
          ),
          Text(
            _detail.empDepartment,
            style: TextStyle(fontSize: 12, color: Colors.grey),
          ),
        ],
      ),
    );
  }

  Widget _loadingPopup() {
    return Container(
      margin: EdgeInsets.only(top: 24, bottom: 16),
      alignment: Alignment.center,
      child: CircularProgressIndicator(
        backgroundColor: Colors.grey,
        valueColor: AlwaysStoppedAnimation<Color>(
          Colors.blue,
        ),
      ),
    );
  }

  void _getEmployeeList() async {
    if (!_hasMore) return;
    setState(() {
      _isLoading = true;
    });
    await new Future.delayed(new Duration(seconds: 2));
    String jsonString = await _loadEmployeeData();
    var jsonResult = json.decode(jsonString);
    if (jsonResult != null) {
      var list =
          jsonResult.map<Employee>((json) => Employee.fromJson(json)).toList();
      _employeeList.addAll(list);
      _isLoading = false;
    } else {
      _hasMore = false;
    }
    if (mounted) setState(() {});
  }

  Future<String> _loadEmployeeData() async {
    return await rootBundle.loadString('assets/resources/json/employee.json');
  }
}
100 views0 comments

Recent Posts

See All

コメント


Post: Blog2_Post
bottom of page