-1

Given the official Flutter codelabs with the startup name generator, I have added a FutureBuilder and data from a HTTP GET request.

I have a button that adds an item to a "favorite" list. Another screen has a list with all the favorites. When removing a favorite from this second screen, the state in the first screen is not updated. What am I missing?

myScreen.dart:

import 'dart:convert';

import 'package:testapp/models/User.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:testapp/screens/favoriteList.dart';

class MyScreen extends StatefulWidget {
  @override
  _MyScreenState createState() => _MyScreenState();
}

class _MyScreenState extends State<MyScreen> {
  Future<List<User>> _listFuture;
  Set<User> _saved = {};

  @override
  void initState() {
    super.initState();
    _listFuture = fetchUsers(http.Client());
  }

  Future<Null> refreshList() async {
    setState(() {
      _listFuture = fetchUsers(http.Client());
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Test'),
        actions: [
          IconButton(icon: Icon(Icons.list), onPressed: _pushSaved),
        ],
      ),
      body: Container(
        child: new FutureBuilder<List<User>>(
          future: _listFuture,
          builder: (context, snapshot) {
            if (snapshot.connectionState == ConnectionState.waiting) {
              return new Center(
                child: new CircularProgressIndicator(),
              );
            } else if (snapshot.hasError) {
              return new Text("Error");
            } else {
              final items = snapshot.data ?? <User>[];
              return new Scrollbar(
                  child: new RefreshIndicator(
                      child: ListView.builder(
                        itemCount: items.length,
                        itemBuilder: (context, index) {
                          return _buildRow(items[index]);
                        },
                      ),
                      onRefresh: refreshList));
            }
          },
        ),
      ),
    );
  }

  Widget _buildRow(User user) {
    final alreadySaved = _saved.contains(user);
    return Container(
        child: Card(
      child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
        ListTile(title: Text(user.firstName)),
        IconButton(
            icon: Icon(
              alreadySaved ? Icons.favorite : Icons.favorite_border,
            ),
            onPressed: () {
              setState(() {
                if (alreadySaved) {
                  _saved.remove(user);
                } else {
                  _saved.add(user);
                }
              });
            }),
      ]),
    ));
  }

  void _pushSaved() {
    Navigator.of(context).push(MaterialPageRoute<void>(
      builder: (BuildContext context) => FavoriteList(data: _saved),
    ));
  }

  Future<List<User>> fetchUsers(http.Client client) async {
    var response = await http.get(Uri.https("reqres.in", "api/users"));

    var body = json.decode(response.body);

    List<User> users =
        List<User>.from(body['data'].map((model) => User.fromJson(model)));

    return users;
  }
}

favoriteList.dart:

import 'package:testapp/models/User.dart';
import 'package:flutter/material.dart';

class FavoriteList extends StatefulWidget {
  final Set<User> data;
  FavoriteList({Key key, this.data}) : super(key: key);

  @override
  _FavoriteListState createState() => _FavoriteListState();
}

class _FavoriteListState extends State<FavoriteList> {
  Set<User> _saved;

  @override
  void initState() {
    _saved = widget.data;
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    final Iterable<ListTile> tiles = _saved.map(
      (User user) {
        return ListTile(
          title: Row(
            children: <Widget>[
              Text(
                user.firstName,
              ),
              IconButton(
                icon: const Icon(Icons.delete),
                onPressed: () {
                  setState(() {
                    _saved.remove(user);
                  });
                },
              )
            ],
          ),
        );
      },
    );

    final List<Widget> divided = ListTile.divideTiles(
      context: context,
      tiles: tiles,
    ).toList();

    return Scaffold(
      appBar: AppBar(
        title: const Text('Saved users'),
      ),
      body: ListView(children: divided),
    );
  }
}

User.dart:

class User {
  int id;
  String email;
  String firstName;

  User(int id, String email, String firstName) {
    this.id = id;
    this.email = email;
    this.firstName = firstName;
  }

  User.fromJson(Map json)
      : id = json['id'],
        email = json['email'],
        firstName = json['first_name'];

  Map toJson() {
    return {'id': id, 'email': email, 'firstName': firstName};
  }
}
Kristoffer Jälén
  • 4,112
  • 3
  • 30
  • 54
  • I sorted it out by passing a callback, as done in [this answer](https://stackoverflow.com/questions/51463906/emit-the-data-to-parent-widget-in-flutter). – Kristoffer Jälén Apr 24 '21 at 08:15

1 Answers1

0

I added a callback in the first screen:

void _callback(saved) {
    setState(() {
      _saved = saved;
    });
}

and passed that to the constructor:

void _pushSaved() {
    Navigator.of(context).push(MaterialPageRoute<void>(
      builder: (BuildContext context) => FavoriteList(
        saved: _saved,
        callback: _callback,
      ),
    ));
  }

I added the callback function to the second screen:

class FavoriteList extends StatefulWidget {
  final Set<User> saved;
  final Function(Set<Release>) callback;
  FavoriteList({Key key, this.saved, this.callback}) : super(key: key);

  @override
  _FavoriteListState createState() => _FavoriteListState();
}

and invoked the function:

setState(() {
   widget.saved.remove(release);
   widget.callback(widget.saved);
});
Kristoffer Jälén
  • 4,112
  • 3
  • 30
  • 54