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

Help: Change Areas Based on Provider Notifier #29

Closed cliftonlabrum closed 1 year ago

cliftonlabrum commented 1 year ago

Forgive me if I'm overlooking something simple, but I'm really new to Flutter.

I have a MultiSplitViewController that has two panes: Master() and Detail(). I have a Notifier in place that changes when a user clicks on a menu item.

When the menu is set to the Dashboard, I want my areas to be:

[Area(weight: .75), Area(weight: .25)]

Otherwise, I want it to be:

[Area(weight: .25), Area(weight: .75)]

My notifier works elsewhere in my app, but I can't get the areas property to be reactive. Here is my code:

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

  @override
  Widget build(BuildContext context) {
    var menuProvider = Provider.of<MenuNotifier>(context);

    //Dashboard Split View ---
    MultiSplitViewController splitViewController = MultiSplitViewController(
      areas: menuProvider.menu == 'Dashboard'
          ? [Area(weight: .75), Area(weight: .25)]
          : [Area(weight: .25), Area(weight: .75)],
    );

    MultiSplitView splitView = MultiSplitView(
      dividerBuilder:
          (axis, index, resizable, dragging, highlighted, themeData) {
        return ...
      },
      controller: splitViewController,
      children: const [
        Master(),
        Detail(),
      ],
    );

    MultiSplitViewTheme theme = MultiSplitViewTheme(
      data: MultiSplitViewThemeData(dividerThickness: 16),
      child: splitView,
    );

    //===================================
    return MaterialApp(
      home: Scaffold(
        body: Row(
          children: [
            const Menu(),
            Flexible(
              flex: 1,
              child: theme,
            ),
          ],
        ),
      ),
    );
  }
}

Any idea what I'm doing wrong? Thank you!

caduandrade commented 1 year ago

Hi @cliftonlabrum! Welcome to Flutter. I hope you enjoy it :wink:

I'm removing for a while the Provider from the example because I don't know how to use this package ok? It's not from Flutter core.

Maybe the Provider is not rebuilding the MyApp. Maybe will need something like a listener.

But I found another potential problem. You are not using a stateful widget. So when rebuilding the screen, the position of the dividers will be lost as it would always be creating a new controller. It is necessary to keep the controller in the state.

class MyApp extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => MyAppState();
}

class MyAppState extends State<MyApp> {
  final MultiSplitViewController _splitViewController =
      MultiSplitViewController();

  bool _viewFlag = true;

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

  void _onViewFlagChange(bool? value) {
    setState(() => _viewFlag = value!);

    // does not need be in setState because when setting the areas it already
    // notifies and rebuilds the MultiSplitView
    _updateAreas();
  }

  void _updateAreas() {
    _splitViewController.areas = _viewFlag
        ? [Area(weight: .75), Area(weight: .25)]
        : [Area(weight: .25), Area(weight: .75)];
  }

  @override
  Widget build(BuildContext context) {
    print('building...');
    MultiSplitView splitView = MultiSplitView(
      controller: _splitViewController,
      children: const [
        Placeholder(color: Colors.blue),
        Placeholder(color: Colors.green)
      ],
    );

    MultiSplitViewTheme theme = MultiSplitViewTheme(
      data: MultiSplitViewThemeData(dividerThickness: 16),
      child: splitView,
    );

    //===================================
    return MaterialApp(
      home: Scaffold(
        body: Row(
          children: [
            Checkbox(value: _viewFlag, onChanged: _onViewFlagChange),
            ElevatedButton(
                onPressed: () => setState(() {}), child: Text('Force rebuild')),
            Flexible(
              flex: 1,
              child: theme,
            ),
          ],
        ),
      ),
    );
  }
}

One way to use it in a stateless widget would be to build it with the initialAreas parameter. In this case, the controller is built internally and the next rebuild would not change the areas. So I believe this is not suitable for Provider (if I understand correctly its usage)

    MultiSplitView splitView = MultiSplitView(
      initialAreas: _viewFlag
          ? [Area(weight: .75), Area(weight: .25)]
          : [Area(weight: .25), Area(weight: .75)],
      children: const [
        Placeholder(color: Colors.blue),
        Placeholder(color: Colors.green)
      ],
    );

I don't know if I helped. I would have to study Provider first.

caduandrade commented 1 year ago

Let me know if you handled it. Anything I try to help you with Provider.

cliftonlabrum commented 1 year ago

Sorry for the delay, I've been traveling. Thank you so much! This has been super helpful! Not having this all set up in a StatefulWidget was the problem. The provider is working well to alter the state and update the areas. Thanks again!