From bb76b7f752a316ad1ffb6eb0e5806f32cf14361c Mon Sep 17 00:00:00 2001 From: Ayad Date: Mon, 10 Feb 2025 13:50:26 +0200 Subject: [PATCH 1/2] fix: Correct resizing behavior for RTL direction with vertical separator Fixed an issue where `ResizableWidget` behaved incorrectly when resizing in `TextDirection.rtl`. Previously, resizing in RTL mode moved in the opposite direction of the mouse movement when the widget was structured as a row. This update ensures that the mouse offset is correctly reversed in RTL mode, providing expected resizing behavior. --- lib/src/resizable_widget_args_info.dart | 2 + lib/src/resizable_widget_controller.dart | 8 +-- lib/src/resizable_widget_model.dart | 81 +++++++----------------- lib/src/separator.dart | 4 +- lib/src/separator_controller.dart | 8 +-- 5 files changed, 34 insertions(+), 69 deletions(-) diff --git a/lib/src/resizable_widget_args_info.dart b/lib/src/resizable_widget_args_info.dart index 2e43b6b..c62641b 100644 --- a/lib/src/resizable_widget_args_info.dart +++ b/lib/src/resizable_widget_args_info.dart @@ -21,4 +21,6 @@ class ResizableWidgetArgsInfo { separatorSize = widget.separatorSize, separatorColor = widget.separatorColor, onResized = widget.onResized; + + bool get isVerticalSeparator => !isHorizontalSeparator; } diff --git a/lib/src/resizable_widget_controller.dart b/lib/src/resizable_widget_controller.dart index f0f86a3..f656a50 100644 --- a/lib/src/resizable_widget_controller.dart +++ b/lib/src/resizable_widget_controller.dart @@ -21,15 +21,15 @@ class ResizableWidgetController { _model.callOnResized(); } - void resize(int separatorIndex, Offset offset) { - _model.resize(separatorIndex, offset); + void resize(BuildContext context, int separatorIndex, Offset offset) { + _model.resize(context, separatorIndex, offset); eventStream.add(this); _model.callOnResized(); } - void tryHideOrShow(int separatorIndex) { - final result = _model.tryHideOrShow(separatorIndex); + void tryHideOrShow(BuildContext context, int separatorIndex) { + final result = _model.tryHideOrShow(context, separatorIndex); if (result) { eventStream.add(this); diff --git a/lib/src/resizable_widget_model.dart b/lib/src/resizable_widget_model.dart index 3971178..97f813f 100644 --- a/lib/src/resizable_widget_model.dart +++ b/lib/src/resizable_widget_model.dart @@ -11,20 +11,17 @@ class ResizableWidgetModel { final ResizableWidgetArgsInfo _info; final children = []; double? maxSize; - double? get maxSizeWithoutSeparators => maxSize == null - ? null - : maxSize! - (children.length ~/ 2) * _info.separatorSize; + + double? get maxSizeWithoutSeparators => maxSize == null ? null : maxSize! - (children.length ~/ 2) * _info.separatorSize; ResizableWidgetModel(this._info); void init(SeparatorFactory separatorFactory) { final originalChildren = _info.children; final size = originalChildren.length; - final originalPercentages = - _info.percentages ?? List.filled(size, 1 / size); + final originalPercentages = _info.percentages ?? List.filled(size, 1 / size); for (var i = 0; i < size - 1; i++) { - children.add(ResizableWidgetChildData( - originalChildren[i], originalPercentages[i])); + children.add(ResizableWidgetChildData(originalChildren[i], originalPercentages[i])); children.add(ResizableWidgetChildData( separatorFactory.call(SeparatorArgsBasicInfo( 2 * i + 1, @@ -35,14 +32,11 @@ class ResizableWidgetModel { )), null)); } - children.add(ResizableWidgetChildData( - originalChildren[size - 1], originalPercentages[size - 1])); + children.add(ResizableWidgetChildData(originalChildren[size - 1], originalPercentages[size - 1])); } void setSizeIfNeeded(BoxConstraints constraints) { - final max = _info.isHorizontalSeparator - ? constraints.maxHeight - : constraints.maxWidth; + final max = _info.isHorizontalSeparator ? constraints.maxHeight : constraints.maxWidth; var isMaxSizeChanged = maxSize == null || maxSize! != max; if (!isMaxSizeChanged || children.isEmpty) { return; @@ -62,44 +56,21 @@ class ResizableWidgetModel { } } - void resize(int separatorIndex, Offset offset) { - final leftSize = _resizeImpl(separatorIndex - 1, offset); - final rightSize = _resizeImpl(separatorIndex + 1, offset * (-1)); - - if (leftSize < 0) { - _resizeImpl( - separatorIndex - 1, - _info.isHorizontalSeparator - ? Offset(0, -leftSize) - : Offset(-leftSize, 0)); - _resizeImpl( - separatorIndex + 1, - _info.isHorizontalSeparator - ? Offset(0, leftSize) - : Offset(leftSize, 0)); - } - if (rightSize < 0) { - _resizeImpl( - separatorIndex - 1, - _info.isHorizontalSeparator - ? Offset(0, rightSize) - : Offset(rightSize, 0)); - _resizeImpl( - separatorIndex + 1, - _info.isHorizontalSeparator - ? Offset(0, -rightSize) - : Offset(-rightSize, 0)); + void resize(BuildContext context, int separatorIndex, Offset offset) { + if (_info.isVerticalSeparator && Directionality.of(context) == TextDirection.rtl) { + // Reverse offset if separator is vertical and context direction is Right-to-Left. + offset *= -1; } + + _resizeImpl(separatorIndex - 1, offset); + _resizeImpl(separatorIndex + 1, offset * (-1)); } void callOnResized() { - _info.onResized?.call(children - .where((x) => x.widget is! Separator) - .map((x) => WidgetSizeInfo(x.size!, x.percentage!)) - .toList()); + _info.onResized?.call(children.where((x) => x.widget is! Separator).map((x) => WidgetSizeInfo(x.size!, x.percentage!)).toList()); } - bool tryHideOrShow(int separatorIndex) { + bool tryHideOrShow(BuildContext context, int separatorIndex) { if (_info.isDisabledSmartHide) { return false; } @@ -116,21 +87,15 @@ class ResizableWidgetModel { final coefficient = isLeft ? 1 : -1; if (_isNearlyZero(size)) { // show - final offsetScala = - maxSize! * (target.hidingPercentage ?? target.defaultPercentage!) - - size; - final offset = _info.isHorizontalSeparator - ? Offset(0, offsetScala * coefficient) - : Offset(offsetScala * coefficient, 0); - resize(separatorIndex, offset); + final offsetScala = maxSize! * (target.hidingPercentage ?? target.defaultPercentage!) - size; + final offset = _info.isHorizontalSeparator ? Offset(0, offsetScala * coefficient) : Offset(offsetScala * coefficient, 0); + resize(context, separatorIndex, offset); } else { // hide target.hidingPercentage = target.percentage!; final offsetScala = maxSize! * target.hidingPercentage!; - final offset = _info.isHorizontalSeparator - ? Offset(0, -offsetScala * coefficient) - : Offset(-offsetScala * coefficient, 0); - resize(separatorIndex, offset); + final offset = _info.isHorizontalSeparator ? Offset(0, -offsetScala * coefficient) : Offset(-offsetScala * coefficient, 0); + resize(context, separatorIndex, offset); } return true; @@ -138,10 +103,8 @@ class ResizableWidgetModel { double _resizeImpl(int widgetIndex, Offset offset) { final size = children[widgetIndex].size ?? 0; - children[widgetIndex].size = - size + (_info.isHorizontalSeparator ? offset.dy : offset.dx); - children[widgetIndex].percentage = - children[widgetIndex].size! / maxSizeWithoutSeparators!; + children[widgetIndex].size = size + (_info.isHorizontalSeparator ? offset.dy : offset.dx); + children[widgetIndex].percentage = children[widgetIndex].size! / maxSizeWithoutSeparators!; return children[widgetIndex].size!; } diff --git a/lib/src/separator.dart b/lib/src/separator.dart index 210d465..caa75aa 100644 --- a/lib/src/separator.dart +++ b/lib/src/separator.dart @@ -39,7 +39,7 @@ class _SeparatorState extends State { height: _info.isHorizontalSeparator ? _info.size : double.infinity, ), ), - onPanUpdate: (details) => _controller.onPanUpdate(details, context), - onDoubleTap: () => _controller.onDoubleTap(), + onPanUpdate: (details) => _controller.onPanUpdate(context, details), + onDoubleTap: () => _controller.onDoubleTap(context), ); } diff --git a/lib/src/separator_controller.dart b/lib/src/separator_controller.dart index 8dd0579..c388f34 100644 --- a/lib/src/separator_controller.dart +++ b/lib/src/separator_controller.dart @@ -7,11 +7,11 @@ class SeparatorController { const SeparatorController(this._index, this._parentController); - void onPanUpdate(DragUpdateDetails details, BuildContext context) { - _parentController.resize(_index, details.delta); + void onPanUpdate(BuildContext context, DragUpdateDetails details) { + _parentController.resize(context, _index, details.delta); } - void onDoubleTap() { - _parentController.tryHideOrShow(_index); + void onDoubleTap(BuildContext context) { + _parentController.tryHideOrShow(context, _index); } } From d9399c4b9b0c9cacaf78568494f4266189787594 Mon Sep 17 00:00:00 2001 From: Ayad Date: Mon, 10 Feb 2025 13:57:22 +0200 Subject: [PATCH 2/2] feat: Enhance example to support both LTR and RTL directions Updated the example to demonstrate `ResizableWidget` behavior in both `TextDirection.ltr` and `TextDirection.rtl`, ensuring proper functionality in different text directions. --- example/lib/main.dart | 77 +++++++++++++++++++++++++++---------------- 1 file changed, 49 insertions(+), 28 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index 3400ddb..706e083 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -11,6 +11,7 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( + debugShowCheckedModeBanner: false, title: 'Resizable Widget Example', theme: ThemeData.dark(), home: const MyPage(), @@ -18,43 +19,63 @@ class MyApp extends StatelessWidget { } } -class MyPage extends StatelessWidget { + +class MyPage extends StatefulWidget { const MyPage({Key? key}) : super(key: key); + @override + State createState() => _MyPageState(); +} + +class _MyPageState extends State { + var textDirection = TextDirection.rtl; + @override Widget build(BuildContext context) { + final rtl = textDirection == TextDirection.rtl; return Scaffold( appBar: AppBar( - title: const Text('Resizable Widget Example'), - ), - body: ResizableWidget( - isHorizontalSeparator: false, - isDisabledSmartHide: false, - separatorColor: Colors.white12, - separatorSize: 4, - onResized: _printResizeInfo, - children: [ - Container(color: Colors.greenAccent), - ResizableWidget( - isHorizontalSeparator: true, - separatorColor: Colors.blue, - separatorSize: 10, - children: [ - Container(color: Colors.greenAccent), - ResizableWidget( - children: [ - Container(color: Colors.greenAccent), - Container(color: Colors.yellowAccent), - Container(color: Colors.redAccent), - ], - percentages: const [0.2, 0.5, 0.3], - ), - Container(color: Colors.redAccent), - ], + title: Text('Resizable Widget Example (${rtl? 'Right-to-Left' : 'Left-to-Right'})'), + actions: [ + IconButton( + icon: Icon(rtl ? Icons.subdirectory_arrow_left : Icons.subdirectory_arrow_right), + onPressed: () { + setState(() => textDirection = rtl? TextDirection.ltr : TextDirection.rtl); + }, ), - Container(color: Colors.redAccent), ], ), + body: Directionality( + textDirection: textDirection, + child: ResizableWidget( + isHorizontalSeparator: false, + isDisabledSmartHide: false, + separatorColor: Colors.white12, + separatorSize: 4, + onResized: _printResizeInfo, + children: [ + Container(color: Colors.greenAccent), + ResizableWidget( + isHorizontalSeparator: true, + separatorColor: Colors.blue, + separatorSize: 10, + children: [ + Container(color: Colors.greenAccent), + ResizableWidget( + children: [ + Container(color: Colors.greenAccent), + Container(color: Colors.yellowAccent), + Container(color: Colors.redAccent), + ], + percentages: const [0.2, 0.5, 0.3], + ), + Container(color: Colors.redAccent), + ], + ), + Container(color: Colors.redAccent), + ], + ), + ), ); }