• 作者:老汪软件技巧
  • 发表时间:2024-08-27 07:02
  • 浏览量:

一、思路来源

最近需要一个类似 macOS 的 NSSplitView 组件。于是就简单实现了一个,核心思路是约束最大尺寸减去分割组价的尺寸让两边✖️显示百分比实现。同时支持水平和垂直方向;

二、示例

封装组件考虑哪些问题_封装fetch_

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

最后、总结

后来发现网上已经有了第三方包;但是不重要,封装组件的过程也是自我提升的过程。