artflutter / flutter_awesome_select

Forked from https://github.com/davigmacode/flutter_smart_select, updated legacy code, migrated to null safety and more. AwesomeSelect allows you to easily convert your usual form select or dropdown into dynamic page, popup dialog, or sliding bottom sheet with various choices input such as radio, checkbox, switch, chips, or even custom input. Supports single and multiple choice.
https://pub.dev/packages/flutter_awesome_select
MIT License
15 stars 30 forks source link

List items are not refreshed when the items’ source e.g. a change notifier Provider sends an updated list #3

Open absar opened 2 years ago

absar commented 2 years ago

List items are not refreshed when the items’ source e.g. a change notifier Provider sends an updated list. Rather the list disappears, or it does not refresh. Issue, try to select Crypto, it should show the list of cryptos but nothing will happen, then open settings and try to delete an item from the list, before deleting there were 2 items, after deleting there is one item in the settings but there are still two items in SmartSelect as you can see at the end of the Gif, instead it should show just 1 item: IssueLoop

Doing the same activity with fixed version from PR #5 , in the below example you can do both select Crypto and deleting item in the settings will correctly update the list in SmartSelect: FixedLoop

vasilich6107 commented 2 years ago

Hi @absar I released a fresh beta version with improved null safety support Could you provide a code that reproduces the issue so I can create a unit test to cover this issue?

absar commented 2 years ago

Sure I will try create a simple reproducible code, when I have some time.

absar commented 2 years ago

Hi @vasilich6107 Finally found some time to create a simple reproducible code, though it took much more time than fixing the real issue. Anyways, copy the code, make sure to add provider dependency, then follow the instructions in the App. If you run it with this repository you will see the issues in the App but if you run it with my Git repository it should work fine

To run it with my repo change pubspec.yaml:

flutter_awesome_select:
    git:
      url: https://github.com/absar/flutter_awesome_select.git
      path: ./
Reproducible code ```dart import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; import 'package:provider/provider.dart'; import 'package:flutter_awesome_select/flutter_awesome_select.dart'; void main() => runApp(const MyApp()); class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return ChangeNotifierProvider.value( value: CurrencyViewModel(), child: MaterialApp( theme: ThemeData(primarySwatch: Colors.blue), home: const MyHomePage(), ), ); } } class MyHomePage extends StatefulWidget { const MyHomePage({Key? key}) : super(key: key); @override State createState() => _MyHomePageState(); } class _MyHomePageState extends State { Currency? _transactionType; Category? _transactionCategory; @override Widget build(BuildContext context) { final buildTransactionType = Center( child: CurrencyPicker( initialValue: _transactionType, category: _transactionCategory, onSelected: (t) => setState(() => _transactionType = t), onClosed: () => setState(() {}), ), ); return Scaffold( appBar: AppBar(), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text('Press below to select an item'), const SizedBox(height: 20), buildTransactionType, ], ), ), ); } } class CurrencyPicker extends StatefulWidget { const CurrencyPicker({ super.key, required this.initialValue, this.category, this.onSelected, this.onClosed, }); final Currency? initialValue; final Category? category; final ValueChanged? onSelected; final void Function()? onClosed; @override State createState() => _CurrencyPickerState(); } /// Other code is setup, you should concentrate in this class class _CurrencyPickerState extends State { late Category _category; final categoryTabs = Map.fromEntries(categoryList.map((tc) => MapEntry(tc, Text(tc.name)))); @override void initState() { super.initState(); _setCategory(widget.initialValue); } @override Widget build(BuildContext context) { final navigator = Navigator.of(context); final buildCategory = CupertinoSlidingSegmentedControl( groupValue: _category, children: categoryTabs, onValueChanged: (i) => setState(() => _category = i!), ); final currencies = Provider.of(context, listen: true) .currencies .where((it) => it.category.id == _category.id) .map((tt) { return S2Choice(value: tt, title: tt.name); }).toList(); final editCurrencies = IconButton( icon: const Icon(Icons.settings), onPressed: () => navigator .push(MaterialPageRoute(builder: (_) => const CurrencyEditList())), ); Widget _popupHeaderBuilder( BuildContext context, S2SingleState state) { return Column( mainAxisSize: MainAxisSize.min, children: [ AppBar( centerTitle: true, automaticallyImplyLeading: state.modalConfig.isFullPage, title: state.modalTitle, actions: state.modalActions..add(editCurrencies), ), const Padding( padding: EdgeInsets.all(10.0), child: Text( 'For first issue, select Crypto, it should change and show the ' 'list of cryptos but nothing will happen', softWrap: true, style: TextStyle(color: Colors.red), ), ), buildCategory, ], ); } final title = Text( widget.initialValue?.name == null ? 'Press Me' : widget.initialValue!.name, style: const TextStyle(fontSize: 20, color: Colors.blue), ); return SmartSelect.single( title: 'Items', selectedValue: widget.initialValue, choiceItems: currencies, choiceLayout: S2ChoiceLayout.grid, onChange: (t) { widget.onSelected!(t.value); _setCategory(t.value); }, onModalClose: (_, __) { if (widget.onClosed != null) widget.onClosed!(); }, modalConfig: const S2ModalConfig( title: 'For second issue press here =>', type: S2ModalType.bottomSheet, useHeader: true, ), choiceGrid: const SliverGridDelegateWithFixedCrossAxisCount( mainAxisSpacing: 10, crossAxisSpacing: 10, crossAxisCount: 4, mainAxisExtent: 90, ), choiceBuilder: (context, _, choice) => InkWell( onTap: () => choice.select!(!choice.selected), child: Column( mainAxisSize: MainAxisSize.min, children: [Icon(choice.value!.icon), Text(choice.value!.name)], ), ), tileBuilder: (_, state) => InkWell( onTap: state.showModal, child: AbsorbPointer(child: title), ), modalHeaderBuilder: _popupHeaderBuilder, ); } void _setCategory(Currency? transactionType) { _category = transactionType?.category ?? widget.category ?? categoryList.first; } } class CurrencyViewModel with ChangeNotifier { final List currencies = [ Currency( id: 1, name: 'USD', icon: Icons.monetization_on_outlined, category: categoryList.first, ), Currency( id: 2, name: 'EUR', icon: Icons.euro, category: categoryList.first, ), Currency( id: 3, name: 'BTC', icon: Icons.currency_bitcoin, category: categoryList.last, ), Currency( id: 4, name: 'ETH', icon: Icons.currency_yuan, category: categoryList.last, ), ]; Future delete(Currency currency) async { currencies.removeWhere((t) => t.id == currency.id); notifyListeners(); } } class Currency with ChangeNotifier { final int id; final String name; final IconData icon; final Category category; Currency({ required this.id, required this.name, required this.icon, required this.category, }); } class Category with ChangeNotifier { final int id; final String name; Category({ required this.id, required this.name, }); } final categoryList = [ Category(id: 1, name: 'Fiat Currency'), Category(id: 2, name: 'Crypto'), ]; class CurrencyEditList extends StatefulWidget { const CurrencyEditList({Key? key}) : super(key: key); @override State createState() => _CurrencyEditListState(); } class _CurrencyEditListState extends State { final categoryTabs = Map.fromEntries(categoryList.map((tc) => MapEntry(tc, Text(tc.name)))); late Category _category = categoryList.first; @override Widget build(BuildContext context) { final buildCategory = CupertinoSlidingSegmentedControl( groupValue: _category, children: categoryTabs, onValueChanged: (i) => setState(() => _category = i!), ); final currencies = Provider.of(context, listen: true) .currencies .where((it) => it.category.id == _category.id) .map((e) => ListTile( leading: Icon(e.icon), title: Text(e.name), trailing: IconButton( icon: const Icon(Icons.delete), onPressed: () => Provider.of(context, listen: false) .delete(e), ), )) .toList(); return Scaffold( appBar: AppBar(), body: Column( children: [ Padding( padding: const EdgeInsets.all(8.0), child: buildCategory, ), const Padding( padding: EdgeInsets.all(10.0), child: Text( 'Delete any one currency and go back, it should refresh the list ' 'in previous screens SmartSelect, but it will still show old data, ' 'even though provider is updated', softWrap: true, style: TextStyle(color: Colors.red), ), ), Expanded(child: ListView(children: currencies)), ], ), ); } } ```
vasilich6107 commented 2 years ago

Thanks @absar

FarhanShares commented 1 year ago

Lately, I'm also facing the same issue with the package. @absar if you have fixed it, would you please send a PR to this repo?

absar commented 1 year ago

I actually sent PRs twice in 2021 but were not merged, sent a new one based on latest release, hope it gets merged