A widget to provides horizontal or vertical multiple split view for Flutter.

Overview

Multi split view

A widget to provides horizontal or vertical multiple split view for Flutter.

  • Horizontal or vertical
  • Configurable weight or size for each child
  • Automatic calculation of weights when:
    • Child added without defined weight
    • Weight redistribution when a child is removed
  • Listener to detect children size changes

Usage

Horizontal

    MultiSplitView(children: [child1, child2, child3]);

Vertical

    MultiSplitView(axis: Axis.vertical, children: [child1, child2]);

Horizontal and vertical

    MultiSplitView(axis: Axis.vertical, children: [
      MultiSplitView(children: [child1, child2, child3]),
      child4
    ]);

Setting the weight

    // setting 10% of weight for the first child
    MultiSplitView multiSplitView = MultiSplitView(
        children: [child1, child2, child3],
        controller: MultiSplitViewController(initialWeights: [0.1]));

Minimal child weight

    MultiSplitView(axis: Axis.vertical, children: [
      MultiSplitView(children: [child1, child2], minimalWeight: .40),
      MultiSplitView(children: [child3, child4])
    ]);

Minimal child size in pixels

Used if minimalWeight has not been set. The size will be converted into weight and will respect the limit defined by the MultiSplitView.defaultMinimalWeight constant, allowing all children to be visible.

    MultiSplitView(axis: Axis.vertical, children: [
      MultiSplitView(children: [child1, child2], minimalSize: 100),
      MultiSplitView(children: [child3, child4])
    ]);

Resizable

    MultiSplitView(children: [child1, child2, child3], resizable: false);

Listener

    MultiSplitView(
        children: [child1, child2, child3, child4],
        onSizeChange: (childIndex1, childIndex2) => print(
            'Index of children whose size has changed: $childIndex1 and $childIndex2'));

Divider thickness

    MultiSplitView multiSplitView =
        MultiSplitView(children: [child1, child2, child3]);

    MultiSplitViewTheme theme = MultiSplitViewTheme(
        child: multiSplitView,
        data: MultiSplitViewThemeData(dividerThickness: 30));

Divider painters

Allows customizing the divider through the DividerPainter class.

The DividerPainters factory class offers default painters.

Divider - background color

The DividerPainters.background allows setting the background color. The default color is NULL.

    MultiSplitView multiSplitView = MultiSplitView(children: [child1, child2]);

    MultiSplitViewTheme theme = MultiSplitViewTheme(
        child: multiSplitView,
        data: MultiSplitViewThemeData(
            dividerPainter: DividerPainters.background(color: Colors.black)));

Divider - highlighted background color

    MultiSplitView multiSplitView = MultiSplitView(children: [child1, child2]);

    MultiSplitViewTheme theme = MultiSplitViewTheme(
        child: multiSplitView,
        data: MultiSplitViewThemeData(
            dividerPainter: DividerPainters.background(
                color: Colors.grey[200], highlightedColor: Colors.grey[800])));

Dashed divider

    MultiSplitView multiSplitView = MultiSplitView(children: [child1, child2]);

    MultiSplitViewTheme theme = MultiSplitViewTheme(
        child: multiSplitView,
        data: MultiSplitViewThemeData(
            dividerPainter: DividerPainters.dashed(
                color: Colors.deepOrange, highlightedColor: Colors.black)));

Dashed divider - Customizations

    MultiSplitViewTheme theme = MultiSplitViewTheme(
        child: multiSplitView,
        data: MultiSplitViewThemeData(
            dividerPainter: DividerPainters.dashed(
                gap: 30, size: 20, thickness: 3, highlightedThickness: 6)));

Grooved divider 1

    MultiSplitView multiSplitView = MultiSplitView(children: [child1, child2]);

    MultiSplitViewTheme theme = MultiSplitViewTheme(
        child: multiSplitView,
        data: MultiSplitViewThemeData(
            dividerPainter: DividerPainters.grooved1(
                color: Colors.indigo[100]!,
                highlightedColor: Colors.indigo[900]!)));

Grooved divider 1 - Customizations

    MultiSplitViewTheme theme = MultiSplitViewTheme(
        child: multiSplitView,
        data: MultiSplitViewThemeData(
            dividerPainter: DividerPainters.grooved1(
                size: 5,
                highlightedSize: 30,
                thickness: 3,
                highlightedThickness: 6)));

Grooved divider 2

    MultiSplitView multiSplitView = MultiSplitView(children: [child1, child2]);

    MultiSplitViewTheme theme = MultiSplitViewTheme(
        child: multiSplitView,
        data: MultiSplitViewThemeData(
            dividerPainter: DividerPainters.grooved2(
                color: Colors.grey[400]!, highlightedColor: Colors.red)));

Grooved divider 2 - Customizations

    MultiSplitViewTheme theme = MultiSplitViewTheme(
        child: multiSplitView,
        data: MultiSplitViewThemeData(
            dividerPainter: DividerPainters.grooved2(
                gap: 15,
                thickness: 4,
                count: 3,
                highlightedCount: 9,
                strokeCap: StrokeCap.square)));

Divider - Custom painter

It is possible to extend the DividerPainter class to create a painter from scratch.

class MyDividerPainter extends DividerPainter {
  @override
  Map<int, Tween> buildTween() {
    Map<int, Tween> map = super.buildTween();
    // create your tween here, example:
    map[100] = Tween<double>(begin: 1, end: 5);
    return map;
  }

  @override
  void paint(
      {required Axis dividerAxis,
      required bool resizable,
      required bool highlighted,
      required Canvas canvas,
      required Size dividerSize,
      required Map<int, dynamic> animatedValues}) {
    super.paint(
        dividerAxis: dividerAxis,
        resizable: resizable,
        highlighted: highlighted,
        canvas: canvas,
        dividerSize: dividerSize,
        animatedValues: animatedValues);
    double myAnimatedValue = animatedValues[100];
    // ...
  }
}
    MultiSplitView multiSplitView = MultiSplitView(children: [child1, child2]);

    MultiSplitViewTheme theme = MultiSplitViewTheme(
        child: multiSplitView,
        data: MultiSplitViewThemeData(dividerPainter: MyDividerPainter()));
Comments
  • Changes to grooved2 color ignored

    Changes to grooved2 color ignored

    Hello! This is a great widget, thanks for sharing it. I've noticed one little thing, though:

    I'm trying to animate a fade in of the divider like this:

    return AnimatedBuilder(
      animation: _animationCurve!,
      builder: (context, widget) {
        return MultiSplitViewTheme(
          data: MultiSplitViewThemeData(
            dividerPainter: DividerPainters.grooved2(
              color: buttonColor.withAlpha((_animationCurve!.value * 255).round()), 
              highlightedColor: highlightColor
            )
          ),
          child: MultiSplitView(...
    

    I've confirmed that the the color values are getting animated correctly, and it winds up with 0xffd0d0d0 after the animation is complete, but the divider remains completely transparent on screen, even when I force refreshes.

    bug 
    opened by lrmoorejr 9
  • How do I save and restore the weights?

    How do I save and restore the weights?

    onSizeChange gives me two indexes. What do I do with those? I need to get the weights of all the areas so I can save them, but the areas aren't updated until after this is called.

     void _onSizeChange(int childIndex1, int childIndex2) {
        Area tmp = controller.getArea(childIndex1);
        print('$childIndex1 : ${tmp.weight}');   //  <===== old value
    
        tmp = controller.getArea(childIndex2);
        print('$childIndex2 : ${tmp.weight}');   //  <===== old value
      }
    

    The areas get updated after this call, so I guess I could do a Future.delay and then do the save, but it seems this should be simpler. Am I doing it wrong?

    bug enhancement 
    opened by sgehrman 5
  • Minimal size for each child

    Minimal size for each child

    Thanks for your work on this great plugin!

    We have a pretty simple use case: we want column A to be resizable down to a minimum of 100px, and column B to be resizable down to a minimum of 200px, and so on. As far as I can tell this is not readily achievable. If we're missing it, let me know. :)

    enhancement 
    opened by stx 5
  • can we use fix width? by now only percentage is supported

    can we use fix width? by now only percentage is supported

    can we use px width? by now only percentage is supported

    // setting 10% of weight for the first child
    MultiSplitViewController(weights: [0.1]))
    
    // setting 40% of minweight for the first child
    MultiSplitView(children: [child1, child2], minimalWeight: .40),
    

    i want this:

    // setting 250px of weight for the first child
    MultiSplitViewController(width: [250]))
    
    // setting 250px of minweight for the first child
    MultiSplitView(children: [child1, child2], minimalWidth: 250),
    
    opened by xiaosongmao123 5
  • Allow tap gestures on the splitter

    Allow tap gestures on the splitter

    Hi @caduandrade,

    Thanks for this lib as well :)

    I'd like to programatically change the position of the splitter when it is double clicked - reset it to its initial position. I couldn't find a way to detect any clicks on the splitter, am I missing something?

    enhancement 
    opened by ykrasik 4
  • Use children's keys to preserve state when children are added/removed.

    Use children's keys to preserve state when children are added/removed.

    Children are wrapped inside other widgets, so they lose state when new ones are added or old ones are removed. It does not happen, when children are added to/removed from the end of the list. That's why I have updated the example to show the problem more clearly. Using child's key in root wrapping widget (Positioned, in this case) solves the problem and does not seem to cause any new ones.

    bug 
    opened by LiLoGandalph 4
  • RangeError (index): Invalid value: Not in inclusive range 0..1: 2

    RangeError (index): Invalid value: Not in inclusive range 0..1: 2

    Got this: see code to reproduce below.

    #5 GestureRecognizer.invokeCallback package:flutter/…/gestures/recognizer.dart:198 #6 DragGestureRecognizer._checkStart package:flutter/…/gestures/monodrag.dart:429 #7 DragGestureRecognizer.acceptGesture package:flutter/…/gestures/monodrag.dart:349 #8 GestureArenaManager._resolveByDefault package:flutter/…/gestures/arena.dart:251 #9 GestureArenaManager._tryToResolveArena. package:flutter/…/gestures/arena.dart:232 (elided 10 frames from dart:async) Handler: "onStart" Recognizer: HorizontalDragGestureRecognizer#6ebbe debugOwner: GestureDetector start behavior: start

    ════════ Exception caught by gesture ═══════════════════════════════════════════ RangeError (index): Invalid value: Not in inclusive range 0..1: 2

    If I try to resize the rightmost bar I get that crash.

    import 'package:flutter/material.dart';
    import 'package:multi_split_view/multi_split_view.dart';
    
    class MeditationPanel extends StatefulWidget {
      @override
      _MeditationPanelState createState() => _MeditationPanelState();
    }
    
    class _MeditationPanelState extends State<MeditationPanel>
        with AutomaticKeepAliveClientMixin {
      @override
      bool get wantKeepAlive => true;
    
      @override
      Widget build(BuildContext context) {
        super.build(context);
    
        return MultiSplitViewExample();
      }
    }
    
    class MultiSplitViewExample extends StatefulWidget {
      final List<Color> _colors = [
        Colors.red,
        Colors.blue,
        Colors.green,
        Colors.purple,
        Colors.brown,
        Colors.pinkAccent
      ];
    
      @override
      _MultiSplitViewExampleState createState() => _MultiSplitViewExampleState();
    }
    
    class _MultiSplitViewExampleState extends State<MultiSplitViewExample> {
      MultiSplitViewController controller =
          MultiSplitViewController(weights: [0.01]);
    
      @override
      Widget build(BuildContext context) {
        final Widget buttons = Container(
          padding: const EdgeInsets.all(8),
          child: Row(
            children: const [
              Text('stuff'),
            ],
          ),
        );
    
        final List<Widget> rightChildren = List.empty(growable: true);
        for (int i = 0; i < 2; i++) {
          final Widget view = Container(
            color: widget._colors[i % widget._colors.length],
            child: const SingleChildScrollView(
              child: Text('testing'),
            ),
          );
          rightChildren.add(view);
        }
    
        final sidebar = Container(color: Colors.deepOrange);
        final rightbar = Container(color: Colors.deepOrange);
        final bottomBar = Container(color: Colors.grey);
    
        final topBar = MultiSplitView(
          axis: Axis.vertical,
          controller: controller,
          onSizeChange: _onSizeChange,
          children: rightChildren,
        );
    
        final MultiSplitView multiSplitView = MultiSplitView(
          controller: controller,
          onSizeChange: _onSizeChange,
          children: [
            sidebar,
            MultiSplitView(
              axis: Axis.vertical,
              controller: controller,
              onSizeChange: _onSizeChange,
              children: [
                topBar,
                bottomBar,
              ],
            ),
            rightbar,
          ],
        );
    
        return Column(
          children: [buttons, Expanded(child: multiSplitView)],
        );
      }
    
      void _onSizeChange(int childIndex1, int childIndex2) {
        print('change - childIndex1: $childIndex1 - childIndex2: $childIndex2');
      }
    }
    
    enhancement 
    opened by sgehrman 4
  • Help: Change Areas Based on Provider Notifier

    Help: Change Areas Based on Provider Notifier

    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!

    help wanted 
    opened by cliftonlabrum 3
  • Feature Request: 0 size for children

    Feature Request: 0 size for children

    I have a usecase where'd I'd like to stack all the children together as close to their dividers as possible. At the moment minimum size of 0 means "ignore", but I'd like it if it really did mean "0".

    enhancement 
    opened by 0xNF 3
  • How to get areas once weight has changed

    How to get areas once weight has changed

    Can you give an example of how I can get the new area sizes after a weight change?

    I'd like to give users the chance to lock the split point after a resize, but can't find any way using onWeightChange to save the area info. I've got a controller in state but can't see it from onWeightChange function (unless I'm missing something obvious which is ever possible lol)

    thanks Martin

    question 
    opened by martin-grannis 2
  • dividerWidget position above children

    dividerWidget position above children

    Looking through the source code, not 100% sure how easy this would be to implement, but it would be is it possible to implement the position of the border to be 'above' the children within the stack? This would allow the children to fill the total constraints (width or height) and then have the DividerPainter backgroundColor = [Colors.transparent] and only show the border with the highlightedBackgroundColor. This would mean each position is child's left position + 1/2 border thickness and provide for the child to have a border property.

    For example, if we think about how VS Code layout is built the primary-side-bar and editor have a resizable 'border' that appears onHover/onFocus. However, the border appears elevated in front of the primary-side-bar and editor (even though the elevation appears to be 0).

    enhancement 
    opened by jkgeorge89 2
  • dividerPainter doesn't grow when touched when Axis.horizontal

    dividerPainter doesn't grow when touched when Axis.horizontal

    When using a dividerPainter with Axis.vertical, touching the divider causes the divider to 'grow' to the highlightedSize - this is correct behaviour.

    When doing the same with Axis.horizontal, the divider doesn't grow until it is dragged.

    opened by 4bSolutionsLLP 0
  • How to keep the width of a view when the window size changes?

    How to keep the width of a view when the window size changes?

    MultiSplitViewController(areas: [Area(size: 300)]);
    

    I set width of the first view to size of 300, but it changes when the window is maximized. How can I get it fixed?

    question 
    opened by ngugcx 1
  • How dynamic resize in code?

    How dynamic resize in code?

    I can resize in old versions:

    controller: MultiSplitViewController(
        weights: [
          !visible1 && visible2 ?0.0000000000001 : visible1 && !visible2 ?1 :0.5,
          visible1 && !visible2 ?0.0000000000001 : !visible1 && visible2 ?1 :0.5
          ]
    ),
    

    But can't in new version

    controller: MultiSplitViewController(
      areas: [
        Area(weight: !visible1 && visible2 ?0.1 : visible1 && !visible2 ?1 :0.5),
        Area( weight: visible1 && !visible2 ?0.1 : !visible1 && visible2 ?1 :0.5)
      ],
    ),
    
    question 
    opened by Taron133 2
  • Feature Request: Allow divider to push other dividers

    Feature Request: Allow divider to push other dividers

    For an example see codepen: https://codepen.io/steve_walson/pen/oNprgre

    It'd be really nice, as a user, to have the dividers be able to adjust the weights of panes in front and behind if the dragged divider pushes into other ones. It's hard to explain, but hopefully this gif makes it clear:

    DividerPush

    enhancement 
    opened by 0xNF 2
  • Feature suggestion:  Some views shouldn't resize when whole thing resizes

    Feature suggestion: Some views shouldn't resize when whole thing resizes

    I have a sidebar on the right and left and a view in the middle.

    When I resize the window, I only want the middle view to grow or shrink. The other views must be manually resized.

    I also want to specify that the sidebar has a default width in pixels that can resize beyond that if the user drags it.

    I know NSSplitView from Cocoa handles cases like this, but I haven't looked at it in years.

    Also you should have a reasonable default divider painter. Everyone is going to want a decent default. See split_view, they have some grab handles by default that are nice. I just want to set the divider color and a mouse over color, and the default painter can redraw when I move the mouse over it.

    Writing a full featured split view is actually pretty difficult, so be sure your ready for some pain if you plan on updating it to do all this. I made one years ago.

    enhancement 
    opened by sgehrman 4
Owner
Carlos Eduardo Leite de Andrade
Carlos Eduardo Leite de Andrade
📸 Easy to use yet very customizable zoomable image widget for Flutter, Photo View provides a gesture sensitive zoomable widget.

?? Easy to use yet very customizable zoomable image widget for Flutter, Photo View provides a gesture sensitive zoomable widget. Photo View is largely used to show interacive images and other stuff such as SVG.

Blue Fire 1.7k Jan 7, 2023
A vertical tabs package for flutter framework.

Vertical Tabs A vertical tabs package for flutter framework. Getting Started A simple example of usage. to get more examples see Examples directory. T

null 62 Dec 30, 2022
A Flutter Widget that create a horizontal table with fixed column on left hand side.

horizontal_data_table A Flutter Widget that create a horizontal table with fixed column on left hand side. Installation This package is starting to su

May Lau 204 Dec 27, 2022
Flutter widget form select a date in horizontal timeline with customizable styles.

Flutter widget form select a date in horizontal timeline with customizable styles. Getting Started You can use this package when you need to add a dat

Jose Manuel Márquez 158 Dec 2, 2022
A Flutter package which provides helper widgets for selecting single or multiple account/user from the given list.

account_selector A Flutter package which provides helper widgets for selecting single or multiple account/user from a list Supported Dart Versions Dar

Harpreet Singh 49 Oct 7, 2021
Displays a highly customizable week view (or day view) which is able to display events, to be scrolled, to be zoomed-in & out and a lot more !

Displays a highly customizable week view (or day view) which is able to display events, to be scrolled, to be zoomed-in & out and a lot more !

Hugo Delaunay 196 Dec 2, 2022
A flutter horizontal date picker that always shift the selected date to center.

horizontal_center_date_picker A flutter widget provides a horizontal date picker and always aligns selected date in center of the widget. Usage This s

May Lau 5 Jul 2, 2022
A dart package to display a horizontal bar of customisable toggle tabs. Supports iOS and Android.

toggle_bar A dart package to display a horizontal bar of customisable toggle tabs. Supports iOS and Android. Installation Depend on it. dependencies:

Prem Adithya 9 Jul 13, 2022
Widget to let the user search through a keyword string typed on a customizable keyboard in a single or multiple choices list presented as a dropdown in a dialog box or a menu.

searchable_dropdown Widget to let the user search through a keyword string typed on a customizable keyboard in a single or multiple choices list prese

Bobby Stenly Irawan 108 Sep 11, 2022
A highly customizable multiple selection widget with search functionality.

A highly customizable multiple selection widget with search functionality.

null 5 Dec 19, 2022
Flutter ScrollView Observer - a library of widget that can be used to listen for child widgets those are being displayed in the scroll view

Flutter ScrollView Observer - a library of widget that can be used to listen for child widgets those are being displayed in the scroll view

林洵锋 67 Jan 6, 2023
A Flutter widget which synchronize a ScrollView and a custom tab view

scrollable_list_tabview A Flutter widget which synchronize a ScrollView and a custom tab view. The main idea is to create a custom tab view synchroniz

Aswanath C K 0 Apr 12, 2022
A widget with side-by-side source code view.

A widget with side-by-side source code view. Extracted from the flutter-catalog open-source app.

xwei 23 Aug 29, 2022
PowerFileView - A powerful file view widget, support a variety of file types, such as Doc Eexcl PPT TXT PDF and so on, Android is implemented by Tencent X5, iOS is implemented by WKWebView.

PowerFileView - A powerful file view widget, support a variety of file types, such as Doc Eexcl PPT TXT PDF and so on, Android is implemented by Tencent X5, iOS is implemented by WKWebView.

Yao 8 Oct 22, 2022
A flutter plugin which provides Crop Widget for cropping images.

A flutter plugin which provides Crop Widget for cropping images.

Chooyan 97 Jan 5, 2023
A Flutter plugin that provides a WebView widget

WebView for Flutter A Flutter plugin that provides a WebView widget. On iOS the WebView widget is backed by a WKWebView; On Android the WebView widget

Fitem 2 Jul 29, 2022
A multi select form field using alert dialog to select multiple items with checkboxes and showing as chips.

A multi select form field using alert dialog to select multiple items with checkboxes and showing as chips.

Carlos Eugenio Torres 73 Sep 7, 2022
Similar to Weibo dynamics, WeChat circle of friends, nine grid view controls to display pictures. Support single big picture preview.

Similar to Weibo dynamics, WeChat circle of friends, nine grid view controls to display pictures. Support single big picture preview.

Flutter中国开源项目 296 Dec 28, 2022
NestedScrollView: extended nested scroll view to fix following issues.

NestedScrollView: extended nested scroll view to fix following issues.

FlutterCandies 457 Jan 4, 2023