1

I am trying to learn flutter and building a small "shopping list app". For this purpose I save the state of my shopping list to the sharedPreferences for later use. This way I was able to restore the same list after closing and opening the app again, but only after "triggering a rebuild"(?) by starting to type something in a text field, using the following code:

class _ItemChecklistState extends State<ItemChecklist> {
  final List<ShoppingItem> _items = [];
  final _itemNameController = TextEditingController();
  final _amountController = TextEditingController()..text = '1';
  final Map<int, bool> checkedMap = new Map();
  bool _isComposing = false;

  ...

  @override
  Widget build(BuildContext context) {

    // calling the method to "preload" my state from the shared preferences
    _loadPrefs();

    return Scaffold(
        appBar: AppBar(
          title: Text('Shopping List'),
          actions: <Widget>[
            IconButton(
                onPressed: () => _removeCheckedItems(),
                icon: Icon(Icons.remove_done)),
            IconButton(
              icon: const Icon(Icons.remove_circle_outline),
              tooltip: 'Remove all items',
              onPressed: () => _removeAllItems(),
            ),
          ],
        ),
        body: Column(children: [
          Flexible(
            child: ListView.builder(
              itemBuilder: (_, int index) => _items[index],
              padding: EdgeInsets.all(8.0),
              itemCount: _items.length,
            ),
          ),
          Divider(height: 1.0),
          Container(child: _buildTextComposer())
        ]));
  }
  ...

  // the method I use to "restore" my state
  void _loadPrefs() async {
    String key = 'currentItemList';
    SharedPreferences prefs = await SharedPreferences.getInstance();
    if (!prefs.containsKey(key)) { return; }

    _items.clear();
    checkedMap.clear();

    Map stateAsJson = jsonDecode(prefs.getString(key));
    final itemsKey = 'items';
    final checkedMapKey = 'checkedMap';
    List items = stateAsJson[itemsKey];
    Map checkedMapClone = stateAsJson[checkedMapKey];

    for (Map item in items){
      ShoppingItem newItem = ShoppingItem(
        id: item['id'],
        name: item['name'],
        amount: item['amount'],
        removeFunction: _removeItemWithId,
        checkedMap: checkedMap,
        saveState: _saveListToSharedPrefs,
      );
      _items.add(newItem);
      checkedMap.putIfAbsent(newItem.id, () => checkedMapClone[newItem.id.toString()]);
    }
  }
  ...

}

Now at this point loading the state and setting the lists works fine, so _items list is updated correctly, as well as the checkedMap, but the ListView does not contain the corresponding data. How can I for example "trigger a rebuild" immediatlly or make sure that the "first" build of the ListView already contains the correct state? Thanks :)

1 Answer 1

1

You have to use FutureBuilder when your UI depends on a async task

Future<List<ShoppingItem>> _getShoppingItems;

@override
void initState() {
  _getShoppingItems = _loadPrefs();
  super.initState();
}

@override
Widget build(BuildContext context) {
  FutureBuilder<List<ShoppingItem>>(
    future: _getShoppingItems,
    builder: (context, snapshot) {
      // Data not loaded yet
      if (snapshot.connectionState != ConnectionState.done) {
        return CircularProgressIndicator();
      }

      // Data loaded
      final data = snapshot.data;
      return ListView(...);
    }
  }
);

More info : https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.