Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trigger a function from a widget to a State object

Tags:

flutter

dart

This is a simplified version of the scenario:

class ParentWdiegt extends StatelessWidget{
//
//
floatinActionButton: FloatingActionButtonWidget(onPressed:()=>CustomWidgetState.someMethod(someValue))
//
//somewhere in the ParentWidget tree
child: CustomWidget() //is stateful
}

CustomWidgetState

class CustomWidgetState extends State<CustomWidget>{
//trigger this function when FAB is pressed in parent widget
someMethod(SomeValue) {//}
}

Is there any way that I can expose someMethod in the state object to be triggered when FAB is pressed without using InheritedWidget?

like image 366
Shady Aziza Avatar asked Jun 07 '18 05:06

Shady Aziza


People also ask

How do you pass a function from one widget to another in Flutter?

The child calls the function when a button is pressed: import 'package:flutter/material. dart'; class Parent extends StatefulWidget { @override State<StatefulWidget> createState() { return ParentState(); } } class ParentState extends State<Parent> { Color selectedColor = Colors.

Why is the build () method on state and not stateful widget?

Why is the build method on State, and not StatefulWidget? Putting a Widget build(BuildContext context) method on State rather than putting a Widget build(BuildContext context, State state) method on StatefulWidget gives developers more flexibility when subclassing StatefulWidget.

Which functionality is used to change the state of a widget?

Calling setState() is critical, because this tells the framework that the widget's state has changed and that the widget should be redrawn. The function argument to setState() toggles the UI between these two states: A star icon and the number 41.


2 Answers

You can use GlobalKey for that:

// some global place
final customWidgetKey = new GlobalKey<CustomWidgetState>();

...

  // import the file with "customWidgetKey"

  new CustomWidget(key: customWidetKey, ...)

...

  // import the file with "customWidgetKey"

  floatinActionButton: FloatingActionButtonWidget(
      onPressed: ()=>customWidgetKey.currentState.someMethod(someValue))
like image 134
Günter Zöchbauer Avatar answered Oct 04 '22 00:10

Günter Zöchbauer


While GlobalKey allows for an easy access to any widget's state ; avoid it. Widgets should not interact with other widgets directly. This is one of the core principle of Flutter.

Flutter uses reactive programming instead. Where widgets communicate with each others by submitting events. Not by directly editing the desired widget.

The obvious benefit is that widgets stays independant. And potentially dozens of widgets can communicate with each others using the same principle.

I already made an example here on how to make two different widgets share a common editable value.

If you want to call methods instead, this uses the same principle : A Listenable or Stream shared between widgets. But without using AnimatedWidget or StreamBuilder for the listening. Instead we'll do the listening manually (which requires slighly more boilerplate) to trigger a custom function.

Here's an example using Stream.

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

class ParentWidget extends StatefulWidget {
  @override
  _ParentWidgetState createState() => _ParentWidgetState();
}

class _ParentWidgetState extends State<ParentWidget> {
  final changeNotifier = new StreamController.broadcast();

  @override
  void dispose() {
    changeNotifier.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return new Column(
      children: <Widget>[
        new AnotherWidget(
          shouldTriggerChange: changeNotifier.stream,
        ),
        new RaisedButton(
          child: new Text("data"),
          onPressed: () => changeNotifier.sink.add(null),
        )
      ],
    );
  }
}

class AnotherWidget extends StatefulWidget {
  final Stream shouldTriggerChange;

  AnotherWidget({@required this.shouldTriggerChange});

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

class _AnotherWidgetState extends State<AnotherWidget> {
  StreamSubscription streamSubscription;

  @override
  initState() {
    super.initState();
    streamSubscription = widget.shouldTriggerChange.listen((_) => someMethod());
  }

  @override
  didUpdateWidget(AnotherWidget old) {
    super.didUpdateWidget(old);
    // in case the stream instance changed, subscribe to the new one
    if (widget.shouldTriggerChange != old.shouldTriggerChange) {
      streamSubscription.cancel();
      streamSubscription = widget.shouldTriggerChange.listen((_) => someMethod());
    }
  }

  @override
  dispose() {
    super.dispose();
    streamSubscription.cancel();
  }

  void someMethod() {
    print('Hello World');
  }

  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

In this example, someMethod of AnotherWidget will be called whenever a click on the RaisedButton instantiated by _ParentWidgetState is performed.

like image 24
Rémi Rousselet Avatar answered Oct 04 '22 01:10

Rémi Rousselet