Blasanka
Blasanka

Reputation: 22437

Is it possible to change the state of another class using a button which is located at somewhere else in the widget tree

This is my widget tree. I want to change the Text widget in the MemberSection class when I press RaisedButton in the AdminSection class. Both MemberSection and AdminSection are in HomePage class.

enter image description here

Below are dart files:

import 'package:flutter/material.dart';

void main() {
  runApp(
    MaterialApp(
      home: HomePage(),
    ),
  );
}

class HomePage extends StatelessWidget {
  final int count = 0;

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: <Widget>[MemberSection(count), AdminSection(count)],
      ),
    );
  }
}

class MemberSection extends StatefulWidget {
  MemberSection(this.count);

  final int count;

  @override
  _MemberSectionState createState() => new _MemberSectionState();
}

class _MemberSectionState extends State<MemberSection> {
  @override
  Widget build(BuildContext context) {
    return new Text('${widget.count} member(s)');//I need to change this widget
  }
}

class AdminSection extends StatefulWidget {
  AdminSection(this.count);

  final int count;

  @override
  _AdminSectionState createState() => new _AdminSectionState();
}

class _AdminSectionState extends State<AdminSection> {
  void incrementer() {
    setState(() {
      //widget.count++; //cannt do
    });
  }

  void decrementer() {
    setState(() {
      //widget.count--; //cannt do
    });
  }

  @override
  Widget build(BuildContext context) {
    return new Column(
      children: <Widget>[
        Text('${widget.count} admin(s)'),//Also, I need to change this widget
        ButtonBar(
          children: <Widget>[
            RaisedButton(
              child: Text('add a member'),
              onPressed: incrementer,
            ),
            RaisedButton(
              child: Text('delete a member'),
              onPressed: decrementer,
            ),
          ],
        ),
      ],
    );
  }
}

Note: this example for to demonstrate my problem.

I know that I can implement incrementer and decrementer in HomePage class and pass the function down the widget but according to this scenario it cannot be done, because one RaisedButton widgets are in other side of the widget tree.

Update:

Linked duplicate question is different from my question because, I'm not instantiating the MemberSection inside AdminSection

Upvotes: 0

Views: 3874

Answers (1)

Blasanka
Blasanka

Reputation: 22437

Yes, it is possible.

The easiest way is to use InheritedWidget

You may have remember a method of(context), as examples:

MediaQuery.of(context).size
Navigator.of(context).pop()
Theme.of(context).canvasColor

Those are inherited widgets.You are using of() anywhere in your app, you can access your theme, because they're inherited. Using of method you can change the state of your widget tree after you implement your widget tree.

So, your example code should look like below:

import 'package:flutter/material.dart';

void main() {
  runApp(
    MaterialApp(
      home: MyApp(),
    ),
  );
}

//this is a newly added stateful widget to use inherited widget
class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  int count = 0;

  void incrementer() {
    setState(() {
      count++;
    });
  }

  void decrementer() {
    setState(() {
      count--;
    });
  }

  @override
  Widget build(BuildContext context) {
    return CounterState(
      count: count,
      incrementer: incrementer,
      decrementer: decrementer,
      child: HomePage(),
    );
  }
}

//this is the inherited widget
class CounterState extends InheritedWidget {
  CounterState({Key key, this.count, this.incrementer, this.decrementer, Widget child})
      : super(key: key, child: child);

  final int count;
  final Function incrementer;
  final Function decrementer;

  @override
  bool updateShouldNotify(CounterState oldWidget) {
    return count != oldWidget.count;
  }

  static of(BuildContext context) {
    return context.inheritFromWidgetOfExactType(CounterState);
  }
}

//make below classes stateless(not must but useless)
class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: <Widget>[
          MemberSection(),
          AdminSection()
        ],
      ),
    );
  }
}

class MemberSection extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final counterState = CounterState.of(context);
    return new Text('${counterState.count} member(s)');
  }
}

class AdminSection extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final counterState = CounterState.of(context);
    return new Column(
      children: <Widget>[
        Text('${counterState.count} admin(s)'),
        ButtonBar(
          children: <Widget>[
            RaisedButton(child: Text('add a member'), onPressed: counterState.incrementer,),
            RaisedButton(child: Text('delete a member'), onPressed: counterState.decrementer,),
          ],
        ),
      ],
    );
  }
}

One great tutorial can be found here.

Upvotes: 2

Related Questions