0

guys, I'm starting with flutter and I decided to make a mini game like "cookie clicker" to train but I've just run into a problem with the state management...

I use a provider to propagate the "GameController" which aims to update the player's resources and do the calculations.

I manage to get the "GameController" everywhere but when I interact with a resource (object in the "GameController") and this one is up to date but not in the UI, it doesn't change...

main.dart :

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'controllers/GameController.dart';

void main() => runApp(MokaOnline());

class MokaOnline extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (context) => GameController()),
      ],
      child: MokaOnlineApp(),
    );
  }
}

class MokaOnlineApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'MokaOnline',
      home: Test(),
    );
  }
}

class Test extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Consumer<GameController>(
      builder: (context, gameControler, _) {
        final _mokaResource =
            gameControler.essentialResourceController.mokaResource;

        return Container(
          child: Column(
            children: <Widget>[
              Text('Moka : ${_mokaResource.getResource()}'),
              FlatButton(
                onPressed: () {
                  _mokaResource.increment();
                },
                child: Text('Add Moka'),
              )
            ],
          ),
        );
      },
    );
  }
}

GameController.dart :

import 'package:flutter/foundation.dart';
import 'package:mokaonline/controllers/EssentialResourceController.dart';

class GameController {
  EssentialResourceController _essentialResourceController =
      EssentialResourceController();

  EssentialResourceController get essentialResourceController =>
      _essentialResourceController;
}

EssentialResourceController.dart :

import 'package:mokaonline/models/essentialResources/MokaResourceModel.dart';
import 'package:mokaonline/models/essentialResources/ResourceEssentialInterface.dart';

class EssentialResourceController {
  ResourceEssentialInterface _mokaResource = MokaResourceModel();

  ResourceEssentialInterface get mokaResource => _mokaResource;
}

MokaResourceModel.dart :

import 'package:flutter/foundation.dart';
import 'package:mokaonline/models/essentialResources/ResourceEssentialInterface.dart';

class MokaResourceModel extends ChangeNotifier
    implements ResourceEssentialInterface {
  int _moka = 0;

  void increment() {
    _moka++;
    notifyListeners();
  }

  @override
  int getResource() {
    return _moka;
  }
}

I understood that the UI was not refresh because it's not really the "GameController" that is updated... But I don't see how to structure the code otherwise.

Anyway, thank you for your patient and have a nice day!

3 Answers3

2

you are looking for something like nested objects in a provider

like Flutter Provider Nested Objects

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:flutter/foundation.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (context) => MokaResourceModel()),
        ChangeNotifierProxyProvider<MokaResourceModel, EssentialResourceController>(
            create: (_) => EssentialResourceController(),
            update: (_, moka, essR) => essR..mokaResourceModel=moka
        ),
        ChangeNotifierProxyProvider<EssentialResourceController, GameController>(
            create: (_) => GameController(),
            update: (_, essR, gamec) => gamec..essentialResourceController=essR
        )
      ],
      child: MokaOnlineApp(),
    );
  }
}

class MokaOnlineApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'MokaOnline',
      home: Test(),
    );
  }
}

class Test extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Consumer<GameController>(
      builder: (context, gameControler, _) {
        final _mokaResource = gameControler.essentialResourceController.mokaResourceModel;

        return new SafeArea(
          child: Container(
            child: Column(
              children: <Widget>[
                Text('Moka : ${_mokaResource.getResource()}'),
                FlatButton(
                  onPressed: () {
                    _mokaResource.increment();
                  },
                  child: Text('Add Moka'),
                )
              ],
            ),
          ),
        );
      },
    );
  }
}

class GameController extends ChangeNotifier {

  GameController() {_EssentialResourceController = EssentialResourceController();}

  EssentialResourceController _EssentialResourceController = EssentialResourceController();

  EssentialResourceController get essentialResourceController => _EssentialResourceController;

  set essentialResourceController(EssentialResourceController value)
  {
    _EssentialResourceController = value;
    notifyListeners();
  }

}

class EssentialResourceController extends ChangeNotifier {

  EssentialResourceController() {_MokaResourceModel = MokaResourceModel();}

  MokaResourceModel _MokaResourceModel = MokaResourceModel();

  MokaResourceModel get mokaResourceModel => _MokaResourceModel;

  set mokaResourceModel(MokaResourceModel value)
  {
    _MokaResourceModel = value;
    notifyListeners();
  }

}

class MokaResourceModel extends ChangeNotifier {

  int _moka = 0;

  void increment() {
    _moka++;
    notifyListeners();
  }

  @override
  int getResource() {
    return _moka;
  }
}
FloW
  • 1,211
  • 6
  • 13
  • 1
    Thank you very much, that's what I needed! Just one last little thing... Here, the EssentialResourceController is only dependent on MokaResourceModel but if you add WoodResourceModel for example. How do we assign another dependency to it since ChangeNotifierProxyProvider has the required "create" parameter. It will create recreate the EssentialResourceController object, right? Anyway, thanks again! – Monsieur Jack Apr 05 '20 at 20:44
  • do you mean a example where WoodResource is between EssentialResourceController and MokaResourceModel ??.. and the "create" fires only ones at the start of the widget – FloW Apr 05 '20 at 21:17
  • I was thinking more about the fact that EssentialResourceController contains MokaResource and WoodResource. Currently, it only has MokaResource and we set it in the ChangeNotifierProvider. We should be able to do : ChangeNotifierProxyProvider( create: (_) => EssentialResourceController(), update: (_, moka, wood, essR) => essR..mokaResourceModel=moka, essR.. wood = wood ), – Monsieur Jack Apr 05 '20 at 21:34
0

something is wrong, but maybe you find the error

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:flutter/foundation.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (context) => MokaResourceModel()),
        ChangeNotifierProvider(create: (context) => WoodResource()),
        ChangeNotifierProxyProvider2<MokaResourceModel, WoodResource, EssentialResourceController>(
            create: (_) => EssentialResourceController(),
            update: (_, moka, wood, essR) {
                    essR..mokaResourceModel=moka;
                    essR..woodResource=wood;
                  },
        ),
        ChangeNotifierProxyProvider<EssentialResourceController, GameController>(
            create: (_) => GameController(),
            update: (_, essR, gamec) => gamec..essentialResourceController=essR
        )
      ],
      child: MokaOnlineApp(),
    );
  }
}

class MokaOnlineApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'MokaOnline',
      home: Test(),
    );
  }
}

class Test extends StatelessWidget {
  @override
  Widget build(BuildContext context) {


    return Consumer<GameController>(
      builder: (context, gameControler, _) {
        final _moka = gameControler.essentialResourceController.mokaResourceModel;
        final _wood = gameControler.essentialResourceController.woodResource;

        return new SafeArea(
          child: Container(
            child: Column(
              children: <Widget>[
                Text('Moka : ${_moka.getResource()}'),
                FlatButton(
                  onPressed: () {
                    _moka.increment();
                  },
                  child: Text('Add Moka'),
                ),
                new Container(height: 100.0,),
                Text('Wood : ${_wood.getResource()}'),
                FlatButton(
                  onPressed: () {
                    _wood.increment();
                  },
                  child: Text('Add Wood'),
                )
              ],
            ),
          ),
        );
      },
    );
  }
}

class GameController extends ChangeNotifier {

  GameController() {_EssentialResourceController = EssentialResourceController();}

  EssentialResourceController _EssentialResourceController = EssentialResourceController();

  EssentialResourceController get essentialResourceController => _EssentialResourceController;

  set essentialResourceController(EssentialResourceController value)
  {
    _EssentialResourceController = value;
    notifyListeners();
  }

}

class EssentialResourceController extends ChangeNotifier {

  EssentialResourceController() {
    _MokaResourceModel = MokaResourceModel();
    _WoodResource = WoodResource();
  }

  MokaResourceModel _MokaResourceModel = MokaResourceModel();
  MokaResourceModel get mokaResourceModel => _MokaResourceModel;

  set mokaResourceModel(MokaResourceModel value)
  {
    _MokaResourceModel = value;
    notifyListeners();
  }

  WoodResource _WoodResource = WoodResource();
  WoodResource get woodResource => _WoodResource;
  set woodResource(WoodResource value)
  {
    _WoodResource = value;
    notifyListeners();
  }

}

class MokaResourceModel extends ChangeNotifier {

  int _moka = 0;

  void increment() {
    _moka++;
    notifyListeners();
  }

  @override
  int getResource() {
    return _moka;
  }
}

class WoodResource extends ChangeNotifier {

  int _wood = 0;

  void increment() {
    _wood++;
    notifyListeners();
  }

  @override
  int getResource() {
    return _wood;
  }

}
FloW
  • 1,211
  • 6
  • 13
0

this is also not really better

ChangeNotifierProvider(create: (context) => MokaResourceModel()),
        ChangeNotifierProvider(create: (context) => WoodResource()),
        ChangeNotifierProxyProvider<MokaResourceModel, EssentialResourceController>(
            key: UniqueKey(),
            create: (_) => EssentialResourceController(),
            update: (_, moka, essR) => essR..mokaResourceModel=moka
        ),
        ChangeNotifierProxyProvider<WoodResource, EssentialResourceController>(
          key: UniqueKey(),
            create: (_) => EssentialResourceController(),
            update: (_, wood, essR) => essR..woodResource=wood
        ),
        ChangeNotifierProxyProvider<EssentialResourceController, GameController>(
            create: (_) => GameController(),
            update: (_, essR, gamec) => gamec..essentialResourceController=essR
        )
FloW
  • 1,211
  • 6
  • 13