Line data Source code
1 : // Copyright 2023 The terCAD team. All rights reserved. 2 : // Use of this source code is governed by a CC BY-NC-ND 4.0 license that can be found in the LICENSE file. 3 : 4 : import 'package:app_finance/_classes/controller/iterator_controller.dart'; 5 : import 'package:app_finance/_configs/theme_helper.dart'; 6 : import 'package:app_finance/design/wrapper/background_wrapper.dart'; 7 : import 'package:app_finance/design/wrapper/keep_alive_wrapper.dart'; 8 : import 'package:flutter/material.dart'; 9 : 10 : typedef FnListWidget = Widget Function(dynamic item, BuildContext context); 11 : 12 : class BaseListInfiniteWidget extends StatefulWidget { 13 : final InterfaceIterator? stream; 14 : final int batch; 15 : final double width; 16 : final FnListWidget buildListWidget; 17 : 18 0 : const BaseListInfiniteWidget({ 19 : super.key, 20 : required this.stream, 21 : required this.width, 22 : required this.buildListWidget, 23 : this.batch = 25, 24 : }); 25 : 26 0 : @override 27 0 : BaseListInfiniteWidgetState createState() => BaseListInfiniteWidgetState(); 28 : } 29 : 30 : class BaseListInfiniteWidgetState extends State<BaseListInfiniteWidget> { 31 : final ScrollController scrollController = ScrollController(); 32 : bool isLoading = false; 33 : List<dynamic> items = []; 34 : 35 0 : @override 36 : void initState() { 37 0 : super.initState(); 38 0 : _loadItems(); 39 0 : scrollController.addListener(_scrollListener); 40 : } 41 : 42 0 : @override 43 : void dispose() { 44 0 : scrollController.dispose(); 45 0 : super.dispose(); 46 : } 47 : 48 0 : void _loadItems() { 49 0 : setState(() => isLoading = true); 50 0 : Future.delayed(const Duration(milliseconds: 300), () { 51 0 : setState(() { 52 0 : isLoading = false; 53 0 : _addItems(); 54 : }); 55 : }); 56 : } 57 : 58 0 : void _addItems() { 59 0 : if (widget.stream != null) { 60 0 : items.addAll(widget.stream!.chunk(widget.batch)); 61 : } 62 : } 63 : 64 0 : void _scrollListener() { 65 0 : if (scrollController.position.extentAfter < 200 && 66 0 : !isLoading && 67 0 : widget.stream != null && 68 0 : !widget.stream!.isFinished) { 69 0 : _loadItems(); 70 : } 71 : } 72 : 73 0 : void clearState() { 74 0 : setState(() { 75 0 : scrollController.jumpTo(0); 76 0 : items.clear(); 77 0 : _addItems(); 78 : }); 79 : } 80 : 81 0 : @override 82 : Widget build(BuildContext context) { 83 0 : if (widget.stream?.isEmpty == true) { 84 : return ThemeHelper.emptyBox; 85 : } 86 0 : if (widget.stream?.isFirst == true) { 87 0 : WidgetsBinding.instance.addPostFrameCallback((_) => clearState()); 88 : } 89 0 : return ListView.builder( 90 : scrollDirection: Axis.vertical, 91 : shrinkWrap: true, 92 0 : controller: scrollController, 93 0 : itemCount: items.length + 2, 94 0 : itemBuilder: (context, index) { 95 0 : if (index == 0) { 96 : return ThemeHelper.hIndent; 97 0 : } else if (index == items.length + 1) { 98 0 : if (isLoading) { 99 : return const Center(child: CircularProgressIndicator()); 100 : } else { 101 : return ThemeHelper.formEndBox; 102 : } 103 : } else { 104 0 : return KeepAliveWrapper( 105 0 : child: BackgroundWrapper( 106 : index: index, 107 0 : child: widget.buildListWidget( 108 0 : items[index - 1], 109 : context, 110 : ), 111 : ), 112 : ); 113 : } 114 : }, 115 : ); 116 : } 117 : }