Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 49 additions & 28 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,50 +11,71 @@ class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Resizable Widget Example',
theme: ThemeData.dark(),
home: const MyPage(),
);
}
}

class MyPage extends StatelessWidget {

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

@override
State<MyPage> createState() => _MyPageState();
}

class _MyPageState extends State<MyPage> {
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),
],
),
),
);
}

Expand Down
2 changes: 2 additions & 0 deletions lib/src/resizable_widget_args_info.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,6 @@ class ResizableWidgetArgsInfo {
separatorSize = widget.separatorSize,
separatorColor = widget.separatorColor,
onResized = widget.onResized;

bool get isVerticalSeparator => !isHorizontalSeparator;
}
8 changes: 4 additions & 4 deletions lib/src/resizable_widget_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
81 changes: 22 additions & 59 deletions lib/src/resizable_widget_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,17 @@ class ResizableWidgetModel {
final ResizableWidgetArgsInfo _info;
final children = <ResizableWidgetChildData>[];
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,
Expand All @@ -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;
Expand All @@ -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;
}
Expand All @@ -116,32 +87,24 @@ 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;
}

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!;
}

Expand Down
4 changes: 2 additions & 2 deletions lib/src/separator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class _SeparatorState extends State<Separator> {
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),
);
}
8 changes: 4 additions & 4 deletions lib/src/separator_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}