caduandrade / multi_split_view

Provides horizontal or vertical multiple split view for Flutter.
https://caduandrade.github.io/multi_split_view/
MIT License
129 stars 24 forks source link

SetState is not working inside child widget #64

Closed selvam920 closed 1 month ago

selvam920 commented 1 month ago

pls look into this

caduandrade commented 1 month ago

Hi @selvam920!

Could you provide an example code?

selvam920 commented 1 month ago

multi_split_view: 2.4.0 is working, latest version from 3.0.0 causing this issue

caduandrade commented 1 month ago

Sorry, I didn't understand the problem. Did you mean setState of your Widget inside one of the MultiSplitView areas?

I created an example with state:

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

void main() => runApp(const MultiSplitViewExampleApp());

class MultiSplitViewExampleApp extends StatelessWidget {
  const MultiSplitViewExampleApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MultiSplitViewExample(),
    );
  }
}

class MultiSplitViewExample extends StatefulWidget {
  const MultiSplitViewExample({Key? key}) : super(key: key);

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

class MultiSplitViewExampleState extends State<MultiSplitViewExample> {
  final MultiSplitViewController _controller = MultiSplitViewController();

  @override
  void initState() {
    super.initState();
    _controller.areas = [
      Area(widget: const MyWidget()),
      Area(widget: const MyWidget())
    ];
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: MultiSplitViewTheme(
            data: MultiSplitViewThemeData(
                dividerPainter:
                    DividerPainters.background(color: Colors.black)),
            child: MultiSplitView(controller: _controller)));
  }
}

class MyWidget extends StatefulWidget {
  const MyWidget({Key? key}) : super(key: key);

  @override
  State<StatefulWidget> createState() => MyWidgetState();
}

class MyWidgetState extends State<MyWidget> {
  int _count = 0;

  @override
  Widget build(BuildContext context) {
    return Center(
        child: ElevatedButton(
            onPressed: () {
              setState(() {
                _count++;
              });
            },
            child: Text('$_count')));
  }
}
selvam920 commented 1 month ago

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

void main() => runApp(const MultiSplitViewExampleApp());

class MultiSplitViewExampleApp extends StatelessWidget { const MultiSplitViewExampleApp({Key? key}) : super(key: key);

@override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, theme: ThemeData( primarySwatch: Colors.blue, ), home: const MultiSplitViewExample(), ); } }

class MultiSplitViewExample extends StatefulWidget { const MultiSplitViewExample({Key? key}) : super(key: key);

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

class MultiSplitViewExampleState extends State { final MultiSplitViewController _controller = MultiSplitViewController(); int count = 0;

@override void initState() { super.initState(); }

@override Widget build(BuildContext context) { return Scaffold( body: MultiSplitViewTheme( data: MultiSplitViewThemeData( dividerPainter: DividerPainters.background(color: Colors.black)), child: MultiSplitView( initialAreas: [ Area( widget: Center( child: ElevatedButton( onPressed: () { setState(() { count++; }); }, child: const Text('Increase')))), Area( widget: Column( children: [Text(count.toString())], )) ], ))); } }

caduandrade commented 1 month ago

Hi @selvam920 ! Now I see...

As you used the widget attribute, Flutter instantiated the Text using the _count value during the context in which the MultiSplitView was initialized. This Widget was then stored in the State of the MultiSplitView. In the case where you are using outside state values, you need to use the builder attribute.

This case makes me think that maybe it's better not to have the widget attribute and just keep the builder to avoid confusion.

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

void main() => runApp(const MultiSplitViewExampleApp());

class MultiSplitViewExampleApp extends StatelessWidget {
  const MultiSplitViewExampleApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MultiSplitViewExample(),
    );
  }
}

class MultiSplitViewExample extends StatefulWidget {
  const MultiSplitViewExample({Key? key}) : super(key: key);

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

class MultiSplitViewExampleState extends State {
  int _count = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: MultiSplitViewTheme(
            data: MultiSplitViewThemeData(
                dividerPainter:
                    DividerPainters.background(color: Colors.black)),
            child: MultiSplitView(
              initialAreas: [
                Area(
                    widget: Center(
                        child: ElevatedButton(
                            onPressed: () {
                              setState(() {
                                _count++;
                              });
                            },
                            child: const Text('Increase')))),
                Area(
                    builder: (context) => Column(
                          children: [Text(_count.toString())],
                        ))
              ],
            )));
  }
}

Here is an example using pure Flutter to demonstrate what I said about Widget stored in State and using Builder.

import 'package:flutter/material.dart';

void main() => runApp(const App());

class App extends StatelessWidget {
  const App({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const Example(),
    );
  }
}

class Example extends StatefulWidget {
  const Example({Key? key}) : super(key: key);

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

class ExampleState extends State {
  int _count = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Row(crossAxisAlignment: CrossAxisAlignment.stretch, children: [
      Expanded(
          child: Center(
              child: ElevatedButton(
                  onPressed: () {
                    setState(() {
                      _count++;
                    });
                  },
                  child: const Text('Increase')))),
      Expanded(child: MyWidget(child: Center(child: Text(_count.toString())))),
      Expanded(
          child: MyWidget(
              builder: (context) => Center(child: Text(_count.toString()))))
    ]));
  }
}

class MyWidget extends StatefulWidget {
  const MyWidget({Key? key, this.child, this.builder}) : super(key: key);

  final Widget? child;
  final WidgetBuilder? builder;

  @override
  State<StatefulWidget> createState() => MyWidgetState();
}

class MyWidgetState extends State<MyWidget> {
  Widget? _child;
  WidgetBuilder? _builder;

  @override
  void initState() {
    super.initState();
    _builder = widget.builder;
    // This Widget will no longer be updated even with rebuilds
    // of the parent container.
    _child = widget.child;
  }

  @override
  Widget build(BuildContext context) {
    if (_child != null) {
      return _child!;
    }
    return _builder!(context);
  }
}
selvam920 commented 1 month ago

it's working as expected with builder