Raphael Sauer
Raphael Sauer

Reputation: 740

Set StatefulBuilder state outside of StatefulBuilder widget

I'm trying to set a StatefulBuilder widget state outside of its widget. Most examples and the documentation available show the setState method being used inside the widget, however I'm wondering if such method can be called from outside of the StatefulBuilder widget. Here's an example:

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'StackOverflow Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'StackOverflow Example'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return new GestureDetector(
      //Change one of the icon's color using the tap down function
      onTapDown: (TapDownDetails details) {
        return changeColor(details);
      },
      child: Scaffold(
        appBar: AppBar(
          title: Text('Example'),
        ),
        body: Center(
          child: Column(children: [
            //This widget should be rebuilt
            StatefulBuilder(
                builder: (BuildContext context, StateSetter setState)
                {
                  Color _iconColor = Colors.black;
                  return Icon(
                    Icons.money,
                    size: 50,
                    color: _iconColor,
                  );
                }
            ),
            //This icon should not be rebuilt
            Icon(
              Icons.euro,
              size: 50,
              color: Colors.black,
            ),
          ]),
        ),
      ),
    );
  }

  void changeColor(TapDownDetails details) {
    //Rebuilt StatefulBuilder widget here, but how?
    setState(() {
      _iconColor = Colors.green;
    });
  }


}

Currently I get an error because of the _iconColor variable being used in setState. I am also aware that it may be impossible to access it outside of the widget. If that's the case, what would be a better solution to change the icon's color without resorting to rebuilding the whole StatefulWidget?

Thanks for your time.

Upvotes: 3

Views: 3928

Answers (2)

Nisanth Reddy
Nisanth Reddy

Reputation: 6405

This is one way to achieve what you intend, if you have to definitely use the StatefulBuilder.

Basically we are storing the StateSetter that we receive from the StatefulBuilder.builder

class Sample extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return SampleState();
  }
}

class SampleState extends State<Sample> {
  StateSetter internalSetter;
  Color color = Colors.black;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      resizeToAvoidBottomInset: false,
      appBar: AppBar(title: Text('Sample')),
      body: Column(
        children: [
          GestureDetector(
            onTap: () {
              setState(() {
                color = Colors.deepOrange;
              });
            },
            child: Text('Press'),
          ),
          StatefulBuilder(builder: (context, setter) {
            internalSetter = setter;
            return Container(
              height: 100,
              width: 100,
              color: color,
            );
          }),
          Undisturbed(),
        ],
      ),
    );
  }
}

class Undisturbed extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print("Undisturbed is built");
    return Container(
      width: 100,
      height: 100,
      color: Colors.red,
    );
  }
}

Upvotes: 1

darshan
darshan

Reputation: 4569

You can use the ValueListenableBuilder widget.
Example:

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'StackOverflow Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'StackOverflow Example'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
  ValueNotifier _iconColor = ValueNotifier<Color>(Colors.black);
  
  @override
  Widget build(BuildContext context) {
    return new GestureDetector(
      //Change one of the icon's color using the tap down function
      onTapDown: (TapDownDetails details) {
        return changeColor(details);
      },
      child: Scaffold(
        appBar: AppBar(
          title: Text('Example'),
        ),
        body: Center(
          child: Column(children: [
            //This widget should be rebuilt
            ValueListenableBuilder(
            valueListenable: _iconColor,
              builder: (ctx, value, child) {
               return Icon(
                    Icons.money,
                    size: 50,
                    color: value,
                  );
              }
            ),
            //This icon should not be rebuilt
            Icon(
              Icons.euro,
              size: 50,
              color: Colors.black,
            ),
          ]),
        ),
      ),
    );
  }

  void changeColor(TapDownDetails details) =>
    _iconColor.value = Colors.green
}

Upvotes: 9

Related Questions