- 作者:老汪软件技巧
- 发表时间:2024-08-27 07:02
- 浏览量:
一、思路来源
最近需要一个类似 macOS 的 NSSplitView 组件。于是就简单实现了一个,核心思路是约束最大尺寸减去分割组价的尺寸让两边✖️显示百分比实现。同时支持水平和垂直方向;
二、示例
//
// SplitViewDemo.dart
// flutter_templet_project
//
// Created by shang on 2024/8/8 18:17.
// Copyright © 2024/8/8 shang. All rights reserved.
//
import 'package:flutter/material.dart';
import 'package:flutter_templet_project/basicWidget/n_split_view.dart';
import 'package:flutter_templet_project/extension/color_ext.dart';
import 'package:flutter_templet_project/extension/widget_ext.dart';
import 'package:get/get.dart';
class SplitViewDemo extends StatefulWidget {
const SplitViewDemo({
super.key,
this.arguments,
});
final Map<String, dynamic>? arguments;
@override
State createState() => _SplitViewDemoState();
}
class _SplitViewDemoState extends State<SplitViewDemo> {
var direction = Axis.horizontal;
double ratio = 0.5;
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("$widget"),
actions: [
buildExchange(),
],
),
body: buildBody(),
);
}
Widget buildExchange() {
return IconButton(
onPressed: () {
direction =
direction == Axis.horizontal ? Axis.vertical : Axis.horizontal;
ratio = 0.5;
setState(() {});
},
icon: Icon(
Icons.currency_exchange,
),
);
}
Widget buildBody() {
return NSplitView(
direction: direction,
ratio: ratio,
start: buildLeft(),
end: buildRight(),
);
}
Widget buildLeft() {
return Container(
key: ValueKey("left"),
color: ColorExt.random,
child: ListTile(
title: Text("buildLeft" * 9),
),
);
}
Widget buildRight() {
return Container(
key: ValueKey("right"),
color: ColorExt.random,
child: ListTile(
title: Text("buildRight" * 9),
),
);
}
}
三、源码
//
// NSplitView.dart
// flutter_templet_project
//
// Created by shang on 2024/8/8 19:31.
// Copyright © 2024/8/8 shang. All rights reserved.
//
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class NSplitView extends StatefulWidget {
const NSplitView({
super.key,
this.direction = Axis.horizontal,
this.dividerWidth = 16,
this.ratio = 0.5,
required this.start,
required this.end,
});
final Axis direction;
final double dividerWidth;
final double ratio;
final Widget start;
final Widget end;
@override
State createState() => _NSplitViewState();
}
class _NSplitViewState extends State<NSplitView> {
late var direction = widget.direction;
late var dividerWidth = widget.dividerWidth;
//from 0-1
late double ratio = widget.ratio;
late double _total = 0;
double get start => ratio * _total;
double get end => (1 - ratio) * _total;
@override
void initState() {
super.initState();
}
@override
void didUpdateWidget(covariant NSplitView oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.direction != oldWidget.direction ||
widget.dividerWidth != oldWidget.dividerWidth ||
widget.ratio != oldWidget.ratio) {
direction = widget.direction;
dividerWidth = widget.dividerWidth;
ratio = widget.ratio;
setState(() {});
}
}
@override
Widget build(BuildContext context) {
ratio = ratio.clamp(0, 1.0);
if (direction == Axis.horizontal) {
return buildHorizal();
}
return buildVertical();
}
Widget buildHorizal() {
return LayoutBuilder(builder: (context, BoxConstraints constraints) {
assert(ratio <= 1 && ratio >= 0);
if (_total != constraints.maxWidth) {
_total = constraints.maxWidth - dividerWidth;
}
return SizedBox(
width: constraints.maxWidth,
child: Flex(
direction: Axis.horizontal,
children: [
SizedBox(
width: start,
child: buildLeft(),
),
GestureDetector(
behavior: HitTestBehavior.translucent,
child: SizedBox(
width: dividerWidth,
height: constraints.maxHeight,
child: RotationTransition(
turns: AlwaysStoppedAnimation(0.25),
child: Icon(Icons.drag_handle, size: dividerWidth),
),
),
onPanUpdate: (DragUpdateDetails details) {
ratio += details.delta.dx / _total;
ratio = ratio.clamp(0, 1.0);
setState(() {});
},
),
SizedBox(
width: end,
child: buildRight(),
),
],
),
);
});
}
Widget buildVertical() {
return LayoutBuilder(builder: (context, BoxConstraints constraints) {
assert(ratio <= 1 && ratio >= 0);
// if (_totalWidth != constraints.maxHeight) {
// _totalWidth = constraints.maxHeight - _dividerWidth;
// }
_total = constraints.maxHeight - dividerWidth;
return SizedBox(
height: constraints.maxHeight,
child: Flex(
direction: Axis.vertical,
children: [
SizedBox(
height: start,
child: buildLeft(),
),
GestureDetector(
behavior: HitTestBehavior.translucent,
child: Container(
height: dividerWidth,
width: constraints.maxWidth,
child: RotationTransition(
turns: AlwaysStoppedAnimation(0),
child: Icon(Icons.drag_handle, size: dividerWidth),
),
),
onPanUpdate: (DragUpdateDetails details) {
ratio += details.delta.dy / _total;
ratio = ratio.clamp(0, 1.0);
setState(() {});
},
),
SizedBox(
height: end,
child: buildRight(),
),
],
),
);
});
}
Widget buildLeft() {
return widget.start;
}
Widget buildRight() {
return widget.end;
}
}
最后、总结
后来发现网上已经有了第三方包;但是不重要,封装组件的过程也是自我提升的过程。