miércoles, 31 de marzo de 2021

Flutter: StreamBuilder, Pull To Refresh, Pagination

#1

https://medium.com/flutterdevs/pagination-in-flutter-with-firebase-firestore-96d6cc11aef2

#2

#3

https://www.udemy.com/course/flutter-ios-android-fernando-herrera/learn/lecture/14478706#overview

Mi resultado

Esta es mi adaptación aplicada al video anterior + el video 110 StreamBuilder (De la sección 7 Aplicación de películas, del curso de Udemy, flutter-ios-android-fernando-herrera) (ese video es privado) + el tutorial de flutterdevs.

Paginar desde Firebase functions https://fireship.io/lessons/firestore-pagination-guide

const admin = require('firebase-admin');
const pageThree = ref.orderBy(field).limit(10).offset(20);

List view


class DatabasePage extends StatefulWidget {

  static final String routeName = 'pagename';

  @override

  _DatabasePageState createState() => _DatabasePageState();

}


class _DatabasePageState extends State<DatabasePage> {

  ProductsProvider productsProvider = new ProductsProvider();

  ScrollController controller = ScrollController();

  ProductListBloc productListBloc;


  @override

  void initState() {

    super.initState();

    productListBloc = ProductListBloc();

    productListBloc.fetchNextProducts();

    controller.addListener(_scrollListener);

  }


  void _scrollListener() {

    if (controller.offset >= controller.position.maxScrollExtent &&

        !controller.position.outOfRange) {

      print("at the end of list");

      productListBloc.fetchNextProducts();

    }

  }


  @override

  void dispose() {

    productListBloc.dispose();

    super.dispose();

  }


  @override

  Widget build(BuildContext context) {

    return Scaffold(

      appBar: AppBar(

        title: Text('T I T L E'),

        backgroundColor: Color.fromRGBO(92, 171, 115, 50),

        actions: <Widget>[

          IconButton(

            icon: Icon(Icons.add),

            color: Colors.white,

            onPressed: () =>

                Navigator.pushNamed(context, ProductFormPage.routeName)

                    .then((value) {

              setState(() {});

            }),

          )

        ],

      ),

      body: _createList(),

    );

  }


  Widget _createList() {

    return StreamBuilder(

      stream: productListBloc.productStream,

      builder:

          (BuildContext context, AsyncSnapshot<List<ProductModel>> snapshot) {

        if (snapshot.hasData) {

          return ListView.builder(

            controller: controller,

            itemCount: snapshot.data.length + 1,

            itemBuilder: (BuildContext context, int index) {

              if (index < snapshot.data.length) {

                return _createItem(context, snapshot.data[index]);

              } else if (productListBloc.hasMore) {

                return Padding(

                  padding: EdgeInsets.symmetric(vertical: 32.0),

                  child: Center(child: CircularProgressIndicator()),

                );

              } else {

                return Padding(

                  padding: EdgeInsets.symmetric(vertical: 32.0),

                  child: Center(child: Text('Fin de la lista.')),

                );

              }

            },

          );

        } else {

          return Center(child: CircularProgressIndicator());

        }

      },

    );

  }


  Widget _createItem(BuildContext context, ProductModel item) {

    Widget content = ...

    ...

    ...

    ...

    

    return content;

  }


}


BLoC


class ProductListBloc {

  List<ProductModel> documentList = [];

  ProductsProvider productsProvider;

  bool hasMore = true;


  StreamController<List<ProductModel>> _streamController;


  ProductListBloc() {

    _streamController = StreamController<List<ProductModel>>();

    productsProvider = new ProductsProvider();

    hasMore = true;

  }


  Stream<List<ProductModel>> get productStream => _streamController.stream;


  /*This will automatically fetch the next 10 elements from the list*/

  fetchNextProducts() async {

    try {

      List<ProductModel> newDocumentList = (await productsProvider.retrieve(documentList.length));

      documentList.addAll(newDocumentList);

      _streamController.sink.add(documentList);

      hasMore = newDocumentList.length == 30/*page size from backend*/;

    } on SocketException {

      _streamController.sink.addError(SocketException("No Internet Connection"));

    } catch (e) {

      print(e.toString());

      _streamController.sink.addError(e);

    }

  }


  void dispose() {

    _streamController.close();

  }

}