0

I'm having problems with mobx and radiobox: screen don't update when selected. I think it's a silly mistake, here are my main.dart, teste_store.dart and pubspec.yaml. The partial file .g was generated with build_runner and mobx_codegen.

A message appears when I run it: "No observables detected in the build method of Observer". I thought testeStore.selected was an observable and when changes triggers Observer to rebuild.

main.dart

import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';

import 'package:teste_flutter/teste_store.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {

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

class _MyHomePageState extends State<MyHomePage> {

  TesteStore testeStore = TesteStore();

  @override
  Widget build(BuildContext context) {

    List<String> options = ["Option 1", "Option 2", "Option 3"];

    return Scaffold(
      appBar: AppBar(
        title: Text("Test Flutter"),
      ),
      body: Center(
        child: Observer(
          builder: (_){
            return ListView.builder(
              itemCount: options.length,
              itemBuilder: (context, index) {
                return RadioListTile<int>(
                  title: Text(options[index]),
                  value: index,
                  groupValue: testeStore.selected,
                  onChanged: testeStore.changeSelected,
                );
              },
            );
          }
        )
      ),
    );
  }
}

teste_store.dart

import 'package:mobx/mobx.dart';

part 'teste_store.g.dart';

class TesteStore = _TesteStore with _$TesteStore;

abstract class _TesteStore with Store {

  @observable
  int selected = 0;

  @action
  void changeSelected(int newSelected) {
    selected = newSelected;
  }

}

pubspec.yaml

name: teste_flutter
description: A new Flutter application.

publish_to: 'none'

version: 1.0.0+1

environment:
  sdk: ">=2.7.0 <3.0.0"

dependencies:
  flutter:
    sdk: flutter

  cupertino_icons: ^1.0.0
  mobx: ^1.2.1+2
  flutter_mobx: ^1.1.0+2
  provider: ^4.3.2+2

dev_dependencies:
  flutter_test:
    sdk: flutter

  build_resolvers: ^1.3.10
  mobx_codegen: ^1.1.0+1
  build_runner: ^1.10.2

flutter:
  uses-material-design: true

Edit 1 and 2: I put the solution I found here and I shouldn't. Writing down in an answer box.

Rodrigo Lima
  • 103
  • 5
  • Maybe you have broken part file generation? Did you try to re-build it? – BambinoUA Nov 27 '20 at 23:42
  • Yes, I used clean, build and build with that conflicting parameters too. – Rodrigo Lima Nov 27 '20 at 23:53
  • Try to implement counter example from package description as is in your code and see if it is working. – BambinoUA Nov 28 '20 at 07:59
  • @BambinoUA I changed everything to test it, and you got me thinking about how Observer works. Than I put List view inside a Column with a sibling text widget with testeStore.selected like a text, now everything updates. Back to original code I tried to only use print(testeStore.selected); before return inside builder of the Observer and it worked. When I put inside the itemBuilder from RadioListTile don't work. TLDR: I think Observer don't look changes inside other widgets builder. – Rodrigo Lima Nov 28 '20 at 14:25
  • I also thought about this thing. Btw, try to use `ListView` instead of `ListView.builder`. – BambinoUA Nov 28 '20 at 14:32
  • And try to do intermediate assignments in `Observer` builder `final selected = testeStore.selected` and then use new local variable `selected` inside inner builder. – BambinoUA Nov 28 '20 at 14:37
  • Yes but `ListView.builder` is for dynamic data and if I use `ListView` I will need something like a loop to generate the children, isn't it? – Rodrigo Lima Nov 28 '20 at 14:43
  • About that intermediate assignments I think that's the answer for now. Thank you, you helped a lot. – Rodrigo Lima Nov 28 '20 at 14:45
  • Builder is used to lazy loading items, i.e. it loads let say 8 items (depending on viewport size) then while scrolling unloads some items and loads new ones. You may notice that `.builder` ctor contains `itemCount` so you need sone finite list before building `ListView`. Moreover, I noticed your `options` list is finite. And this is just a test. Or wrap with `Observer` not the `List` but `RadioListTile`. – BambinoUA Nov 28 '20 at 14:53

2 Answers2

2

Talking to @BambinoUA, we found out a solution, just to add insight why his solution works.

I did put List view inside a Column with a sibling text widget with testeStore.selected like a text, now everything updates.

child: Observer(
          builder: (_){
            return Column(
              children: [
                Text(testeStore.selected.toString()),
                ListView.builder(
                      itemCount: options.length,
                      itemBuilder: (context, index) {
                        return RadioListTile<int>(
                          title: Text(options[index]),
                          value: index,
                          groupValue: testeStore.selected,
                          onChanged: testeStore.changeSelected,
                        );
                      },
                ),
              ],
            );
          }
        )

Back to original code I tried to only use print(testeStore.selected); before return inside builder of the Observer and it worked:

child: Observer(
          builder: (_){

            print(testeStore.selected);

            return ListView.builder(

When I put inside the itemBuilder from RadioListTile don't work.

child: Observer(
  builder: (_){
    return ListView.builder(
          itemCount: options.length,
          itemBuilder: (context, index) {

            print(testeStore.selected);

            return RadioListTile<int>(

I assume Observer don't look changes inside other widgets builder.

Rodrigo Lima
  • 103
  • 5
1

I see that your TesteStore class is derived from private _TesteStore class. Maybe this is the case? And the error message looks reasonable. Try to make class with @observable public and re-build part file.

Update

  • Try to do intermediate assignments in Observer builder
final selected = testeStore.selected;

and then use new local variable selected inside inner builder.

  • Try to use ListView instead ListView.builder.
  • Try wrap with Observer not the List but RadioListTile.
BambinoUA
  • 6,126
  • 5
  • 35
  • 51