2

I have a Main page with a tabbar with 5 tabs. In Main page, i load a JSON from internet and set different parts of it in each tab. It's a world cup app, showing a tab for each match fase (groups, round of 16, quarterfinals, semifinal, and final). When each tab is loaded, i get the json and build a list view.

In additional, i want to set a button to reload the information (something like a fab or action in appbar). But, when i reload the JSON, how do i setstate of the actual tab?

This is my Main page widget build:

Widget build(BuildContext context) {
return new Scaffold(
  appBar: new AppBar(
    leading: new IconButton(
      icon: new Icon(Icons.arrow_back),
      onPressed: () => Navigator.pop(context),
    ),
    title: new Text(title),
  ),
  body: new Center(
    child: new TabBarView(
      controller: _tabController,
      children: <Widget>[
        new GroupStageMatches(),
        new RoundOfSixteen(),
        new QuarterFinals(),
        new SemiFinal(),
        new Final(),
      ],
    ),
  ),
  bottomNavigationBar: new Material(
    color: Colors.blueAccent,
    child: new TabBar(
      controller: _tabController,
      tabs: <Widget>[
        new Tab(text: "Grupos"),
        new Tab(text: "Oitavas"),
        new Tab(text: "Quartas"),
        new Tab(text: "Semi"),
        new Tab(text:"Final"),
      ],
    ),
  ),
  floatingActionButton: new FloatingActionButton(
    onPressed: () {
      setState(() {
        sing.updateData();
      });
    }
  ),
);}

1 Answers1

1

Most simple solution would be to create valueNotifier in your singleton

  ValueNotifier<String> valueNotifier = new ValueNotifier<String>("");

Then, inside each tab in initState do

sing.valueNotifier.addListener(_valueChanged)

and on dispose clean up listeners.

to notify listeners, you would change the value. If the value is different thatn the previous one it will notify the listeners. This will call _valueChanged method

sing.valueNotifier.value = "HelloWorld";

Once _valueChanged gets called, you can set state in your tab.

EDIT: based on code example

You see, value notifier holds the value that you will need in your class. so once the getJSON resolves, listener will be called in your view.

class Singleton {
  static final Singleton _singleton = new Singleton._internal();
  ValueNotifier<Map<String, dynamic>> groupsJson =
      new ValueNotifier<Map<String, dynamic>>(null);
  ValueNotifier<Map<String, dynamic>> eliminationJson =
      new ValueNotifier<Map<String, dynamic>>(null);

  factory Singleton() {
    return _singleton;
  }

  Singleton._internal() {
    // updateData();
  }


  updateGroupsJson() async {
    _groupsJson.value = await this.getJson(
        "http://www.srgoool.com.br/call?ajax=get_classificacao2&id_fase=1796");
  }

  updateEliminationJson() async {
    _eliminationJson.value = await this.getJson(
        "http://www.srgoool.com.br/call?ajax=get_chaves&id_ano_campeonato=434");
  }

  Future<Map<String, dynamic>> getJson(url) async {
    print("loading");
    final response = await http.get(url);

    if (response.statusCode == 200) {
      print("loaded");
      var teste = json.decode(response.body);
      return teste;
    } else {
      print("erro");
      return null;
    }
  }
}

In your view:

void initState() {
  super.initState();
  sing = new Singleton();
  sing.groupsJson.addListener(onGroupJson);
  sing.updateGroupsJson();

  //you can already draw your frontend with latest groupsJson. Thiss will just update the view when you get a new one
}

void onGroupJson{
  setState ((){
    // assing fields that you need for drawing the view 
  });
}
Tree
  • 29,135
  • 24
  • 78
  • 98
  • But I want to setState of the current tab. In Tab i have a boolean variable called isLoading, which when it's true it shows a CricleProgress and when the data is loaded i want to set it false. – João Pedro Ache Virgili May 27 '18 at 16:49
  • btw, my method updateData() doesnt return anything, it just update the data in mey Singleton (sing is the instance of it). – João Pedro Ache Virgili May 27 '18 at 16:53
  • It should propagate down the tree and change states where it needs redraw. Check this presentation if you want to make it more decoupled. https://www.youtube.com/watch?v=RS36gBEp8OI – Tree May 27 '18 at 16:53
  • Ok, I thought it was calling some async server. Definitely check the presentation I linked, it should shed some light on some cool ideas, to big to write in this small answer. – Tree May 27 '18 at 16:55
  • why 3 ValueNotifier? – João Pedro Ache Virgili May 27 '18 at 17:52
  • just an example, that you can initialize it with value – Tree May 27 '18 at 17:57
  • If you just want to notify listeners, without holding value you can use ChangeNotifier – Tree May 27 '18 at 17:58
  • If you will have a lot of listeners to the same value, you can use streams or even rxDart library like in the presentation – Tree May 27 '18 at 17:58
  • So, let me check if i understand it. ValueNotifier valueNotifier = new ValueNotifier(""); => this is my first value of the variable.||||| sing.valueNotifier.addListener(_valueChanged) => here i'm setting a callback function which will be executed when the variable have the values changed||||||| sing.valueNotifier.value = "HelloWorld"; => here i'm changing the value of the variable, executing the method i passed.||||||| So, i set it on my initState tab and each time i reload json (the variable value) the function i passed will be executed? – João Pedro Ache Virgili May 27 '18 at 18:06
  • Yes correct. Value Notifier holds one value of type . I edited my answer because `new ValueNotifier("");` needs to be string. You can implement ChangeNotifier in your singleton if you want to manualy dispatch changes. You can also check the presentation for more advance usage – Tree May 27 '18 at 18:52
  • hey! the ValueNotifier worked but i'm still getting an error. It's weird! First of all, i'm getting the JSON and building de ListView, OK! I set a fab to reload the json, but when i reload i get "Class 'Future>' has no instance method '[]'.". That's weird because i'm reading the same way as before.. it should work fine – João Pedro Ache Virgili May 27 '18 at 22:51
  • but it works first time, just don't work to the next.. i'm calling the same method! here is the code if want to see, it's in views/matches/matches-group-stage.dart and utils/singleton.dart https://github.com/joaovirgili/flutter_bolao_da_copa – João Pedro Ache Virgili May 27 '18 at 23:13
  • Here is little simpler example – Tree May 27 '18 at 23:32