Flutter个性化堆栈布局插件stack_board_personal的使用

Flutter个性化堆栈布局插件stack_board_personal的使用

stack_board_personal 是一个用于Flutter的自定义堆栈布局插件。通过该插件,你可以轻松地创建复杂的堆栈布局,并且支持自定义元素。

效果预览

预览网址: https://stack.liugl.cn

1. 使用 StackBoardController

首先,你需要初始化一个 StackBoardController,并将其传递给 StackBoard 组件。

import 'package:flutter/material.dart';
import 'package:stack_board_personal/stack_board.dart';

StackBoard(
  controller: _boardController,
  /// 添加背景
  background: const ColoredBox(color: Colors.grey),
),

2. 添加自适应文本

你可以通过 StackBoardControlleradd 方法添加自适应文本。

_boardController.add(
  const AdaptiveText(
    'Flutter Candies',
    tapToEdit: true,
    style: TextStyle(fontWeight: FontWeight.bold),
  ),
);

3. 添加自适应图片

同样地,你可以通过 StackBoardControlleradd 方法添加图片。

_boardController.add(
  StackBoardItem(
    child: Image.network('https://avatars.githubusercontent.com/u/47586449?s=200&v=4'),
  ),
);

4. 添加画板

你也可以添加画板来绘制图形。

_boardController.add(
  const StackDrawing(
    caseStyle: CaseStyle(
      borderColor: Colors.grey,
      iconColor: Colors.white,
      boxAspectRatio: 1,
    ),
  ),
);

5. 添加自定义Widget

你可以添加任何自定义的 Widget 到堆栈布局中。

_boardController.add(
  StackBoardItem(
    child: const Text(
      'Custom Widget',
      style: TextStyle(color: Colors.black),
    ),
    onDel: _onDel,
  ),
);

删除拦截

在删除自定义 Widget 时,可以通过 _onDel 方法进行拦截。

/// 删除拦截
Future<bool> _onDel() async {
  final bool? r = await showDialog<bool>(
    context: context,
    builder: (_) {
      return Center(
        child: SizedBox(
          width: 400,
          child: Material(
            child: Padding(
              padding: const EdgeInsets.all(20),
              child: Column(
                mainAxisSize: MainAxisSize.min,
                children: <Widget>[
                  const Padding(
                    padding: EdgeInsets.only(top: 10, bottom: 60),
                    child: Text('确认删除?'),
                  ),
                  Row(
                    mainAxisAlignment: MainAxisAlignment.spaceAround,
                    children: <Widget>[
                      IconButton(
                        onPressed: () => Navigator.pop(context, true),
                        icon: const Icon(Icons.check)),
                      IconButton(
                        onPressed: () => Navigator.pop(context, false),
                        icon: const Icon(Icons.clear)),
                    ],
                  ),
                ],
              ),
            ),
          ),
        ),
      );
    },
  );

  return r ?? false;
}

6. 添加自定义item

你可以通过继承 StackBoardItem 类来自定义 item,然后通过 StackBoardController 添加这些自定义 item

/// 自定义类型 Custom item type
class CustomItem extends StackBoardItem {
  const CustomItem({
    required this.color,
    Future<bool> Function()? onDel,
    int? id, // &lt;==== 必须
  }) : super(
    child: const Text('CustomItem'),
    onDel: onDel,
    id: id, // &lt;==== 必须
  );

  final Color? color;

  [@override](/user/override) // &lt;==== 必须
  CustomItem copyWith({
    CaseStyle? caseStyle,
    Widget? child,
    int? id,
    Future<bool> Function()? onDel,
    dynamic Function(bool)? onEdit,
    bool? tapToEdit,
    Color? color,
  }) =>
      CustomItem(
        onDel: onDel,
        id: id,
        color: color ?? this.color,
      );
}

使用 StackBoardController 添加自定义 item

import 'dart:math' as math;

...

_boardController.add<CustomItem>(
  CustomItem(
    color: Color((math.Random().nextDouble() * 0xFFFFFF).toInt()),
    onDel: () async => true,
  ),
);

使用 customBuilder 构建自定义 item

StackBoard(
  controller: _boardController,
  /// 如果使用了继承于StackBoardItem的自定义item
  /// 使用这个接口进行重构
  customBuilder: (StackBoardItem t) {
    if (t is CustomItem) {
      return ItemCase(
        key: Key('StackBoardItem${t.id}'), // &lt;==== 必须
        isCenter: false,
        onDel: () async => _boardController.remove(t.id),
        onTap: () => _boardController.moveItemToTop(t.id),
        caseStyle: const CaseStyle(
          borderColor: Colors.grey,
          iconColor: Colors.white,
        ),
        child: Container(
          width: 100,
          height: 100,
          color: t.color,
          alignment: Alignment.center,
          child: const Text(
            'Custom item',
            style: TextStyle(color: Colors.white),
          ),
        ),
      );
    }
  },
)

7. 使用ItemCase进行完全自定义

你可以使用 ItemCase 来实现完全自定义的布局。

Stack(
  children: <Widget>[
    ItemCase(
      isCenter: false,
      child: const Text('Custom case'),
      onDel: () async {},
      onOffsetChanged: (Offset offset) {},
      onSizeChanged: (Size size) {},
      toolConfiguration: const {
        OperatConfiguration.roate: false,
        OperatConfiguration.scale: false
      },
    ),
  ],
)

完整示例代码

以下是完整的示例代码,展示了如何使用 stack_board_personal 插件。

import 'dart:math' as math;

import 'package:flutter/material.dart';
import 'package:stack_board_personal/stack_board_personal.dart';

/// 自定义类型 Custom item type
class CustomItem extends StackBoardItem {
  const CustomItem({
    required this.color,
    Future<bool> Function()? onDel,
    int? id, // &lt;==== 必须
  }) : super(
    child: const Text('CustomItem'),
    onDel: onDel,
    id: id, // &lt;==== 必须
  );

  final Color? color;

  [@override](/user/override) // &lt;==== 必须
  CustomItem copyWith({
    CaseStyle? caseStyle,
    Widget? child,
    int? id,
    Future<bool> Function()? onDel,
    dynamic Function(bool)? onEdit,
    bool? tapToEdit,
    Color? color,
  }) =>
      CustomItem(
        id: id ?? this.id,
        onDel: onDel ?? this.onDel,
        color: color ?? this.color,
      );
}

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  [@override](/user/override)
  Widget build(BuildContext context) {
    return const MaterialApp(home: HomePage());
  }
}

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

  [@override](/user/override)
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  late StackBoardController _boardController;

  [@override](/user/override)
  void initState() {
    super.initState();
    _boardController = StackBoardController();
  }

  [@override](/user/override)
  void dispose() {
    _boardController.dispose();
    super.dispose();
  }

  /// 删除拦截
  Future<bool> _onDel() async {
    final bool? r = await showDialog<bool>(
      context: context,
      builder: (_) {
        return Center(
          child: SizedBox(
            width: 400,
            child: Material(
              child: Padding(
                padding: const EdgeInsets.all(20),
                child: Column(
                  mainAxisSize: MainAxisSize.min,
                  children: <Widget>[
                    const Padding(
                      padding: EdgeInsets.only(top: 10, bottom: 60),
                      child: Text('确认删除?'),
                    ),
                    Row(
                      mainAxisAlignment: MainAxisAlignment.spaceAround,
                      children: <Widget>[
                        IconButton(
                            onPressed: () => Navigator.pop(context, true),
                            icon: const Icon(Icons.check)),
                        IconButton(
                            onPressed: () => Navigator.pop(context, false),
                            icon: const Icon(Icons.clear)),
                      ],
                    ),
                  ],
                ),
              ),
            ),
          ),
        );
      },
    );

    return r ?? false;
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      resizeToAvoidBottomInset: false,
      appBar: AppBar(
        title: const Text('Stack Board Demo'),
        elevation: 0,
      ),
      body: StackBoard(
        controller: _boardController,

        caseStyle: const CaseStyle(
          borderColor: Colors.grey,
          iconColor: Colors.white,
        ),

        /// 背景
        background: ColoredBox(color: Colors.grey[100]!),

        /// 点击取消全部选中状态
        /// tapToCancelAllItem: true,

        /// 如果使用了继承于StackBoardItem的自定义item
        /// 使用这个接口进行重构
        customBuilder: (StackBoardItem t) {
          if (t is CustomItem) {
            List<StackBoardItem>? test;
            CustomItem newTest;
            return ItemCase(
              key: Key('StackBoardItem${t.id}'), // &lt;==== 必须
              isCenter: false,
              onDel: () async => _boardController.remove(t.id),
              onTap: () => {
                print(_boardController.getCurrentSelected([])),
                test = _boardController.getCurrentSelected([t.id]),
                newTest = t.copyWith(color: Colors.black),
                _boardController.changeCurrentSelected([newTest]),
                // _boardController.moveItemToTop(t.id),
              },
              toolConfiguration: const {
                OperatConfiguration.roate: false,
                OperatConfiguration.scale: false
              },
              caseStyle: const CaseStyle(
                borderColor: Colors.grey,
                iconColor: Colors.white,
              ),
              child: Container(
                width: 100,
                height: 100,
                color: t.color,
                alignment: Alignment.center,
                child: const Text(
                  'Custom itemssss',
                  style: TextStyle(color: Colors.white),
                ),
              ),
            );
          }

          return null;
        },
      ),
      floatingActionButton: Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: <Widget>[
          Flexible(
            child: SingleChildScrollView(
              scrollDirection: Axis.horizontal,
              child: Row(
                children: <Widget>[
                  const SizedBox(width: 25),
                  FloatingActionButton(
                    onPressed: () {
                      _boardController.add(
                        const AdaptiveText(
                          'Flutter Candies',
                          tapToEdit: true,
                          style: TextStyle(fontWeight: FontWeight.bold),
                        ),
                      );
                    },
                    child: const Icon(Icons.border_color),
                  ),
                  _spacer,
                  FloatingActionButton(
                    onPressed: () {
                      _boardController.add(
                        StackBoardItem(
                          child: Image.network(
                              'https://avatars.githubusercontent.com/u/47586449?s=200&v=4'),
                        ),
                      );
                    },
                    child: const Icon(Icons.image),
                  ),
                  _spacer,
                  FloatingActionButton(
                    onPressed: () {
                      _boardController.add(
                        const StackDrawing(
                          caseStyle: CaseStyle(
                            borderColor: Colors.grey,
                            iconColor: Colors.white,
                            boxAspectRatio: 1,
                          ),
                        ),
                      );
                    },
                    child: const Icon(Icons.color_lens),
                  ),
                  _spacer,
                  FloatingActionButton(
                    onPressed: () {
                      _boardController.add(
                        StackBoardItem(
                          child: const Text(
                            'Custom Widget',
                            style: TextStyle(color: Colors.black),
                          ),
                          onDel: _onDel,
                          // caseStyle: const CaseStyle(initOffset: Offset(100, 100)),
                        ),
                      );
                    },
                    child: const Icon(Icons.add_box),
                  ),
                  _spacer,
                  FloatingActionButton(
                    onPressed: () {
                      _boardController.add<CustomItem>(
                        CustomItem(
                          color: Color((math.Random().nextDouble() * 0xFFFFFF)
                                  .toInt())
                              .withOpacity(1.0),
                          onDel: () async => true,
                        ),
                      );
                    },
                    child: const Icon(Icons.add),
                  ),
                ],
              ),
            ),
          ),
          FloatingActionButton(
            onPressed: () => _boardController.clear(),
            child: const Icon(Icons.close),
          ),
        ],
      ),
    );
  }

  Widget get _spacer => const SizedBox(width: 5);
}

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

  [@override](/user/override)
  _ItemCaseDemoState createState() => _ItemCaseDemoState();
}

class _ItemCaseDemoState extends State<ItemCaseDemo> {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return Stack(
      children: <Widget>[
        ItemCase(
          isCenter: false,
          child: const Text('Custom case'),
          onDel: () async {},
          onOperatStateChanged: (OperatState operatState) => null,
          onOffsetChanged: (Offset offset) => null,
          onSizeChanged: (Size size) => null,
        ),
      ],
    );
  }
}

更多关于Flutter个性化堆栈布局插件stack_board_personal的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter个性化堆栈布局插件stack_board_personal的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


stack_board_personal 是一个用于 Flutter 的个性化堆栈布局插件,允许开发者创建自定义的堆栈布局,并且可以动态添加、删除和移动子组件。这个插件非常适合需要灵活布局的应用场景,比如拖拽式 UI 构建器、自定义仪表盘等。

安装

首先,你需要在 pubspec.yaml 文件中添加 stack_board_personal 插件的依赖:

dependencies:
  flutter:
    sdk: flutter
  stack_board_personal: ^latest_version

然后运行 flutter pub get 来安装依赖。

基本用法

1. 导入插件

import 'package:stack_board_personal/stack_board_personal.dart';

2. 创建 StackBoard 组件

StackBoard 是插件中的核心组件,用于管理子组件的布局。

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Stack Board Example'),
      ),
      body: StackBoard(
        children: [
          StackBoardItem(
            child: Container(
              color: Colors.red,
              width: 100,
              height: 100,
            ),
            position: Offset(50, 50),
          ),
          StackBoardItem(
            child: Container(
              color: Colors.blue,
              width: 100,
              height: 100,
            ),
            position: Offset(200, 200),
          ),
        ],
      ),
    );
  }
}

3. 动态添加和删除子组件

你可以通过 StackBoardController 来动态管理 StackBoard 中的子组件。

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final StackBoardController _controller = StackBoardController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Stack Board Example'),
      ),
      body: StackBoard(
        controller: _controller,
        children: [
          StackBoardItem(
            child: Container(
              color: Colors.red,
              width: 100,
              height: 100,
            ),
            position: Offset(50, 50),
          ),
        ],
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          _controller.addItem(
            StackBoardItem(
              child: Container(
                color: Colors.green,
                width: 100,
                height: 100,
              ),
              position: Offset(150, 150),
            ),
          );
        },
        child: Icon(Icons.add),
      ),
    );
  }
}

4. 自定义子组件的位置和大小

你可以通过 StackBoardItempositionsize 属性来自定义子组件的位置和大小。

StackBoardItem(
  child: Container(
    color: Colors.yellow,
  ),
  position: Offset(100, 100),
  size: Size(150, 150),
),

5. 拖拽和缩放

默认情况下,StackBoard 支持拖拽和缩放子组件。你可以通过 StackBoardItemisDraggableisResizable 属性来控制是否允许拖拽和缩放。

StackBoardItem(
  child: Container(
    color: Colors.purple,
  ),
  position: Offset(200, 200),
  isDraggable: true,
  isResizable: true,
),

高级用法

1. 自定义手势

你可以通过 StackBoardItemgestureRecognizers 属性来添加自定义手势。

StackBoardItem(
  child: Container(
    color: Colors.orange,
  ),
  position: Offset(300, 300),
  gestureRecognizers: [
    Factory<TapGestureRecognizer>(() => TapGestureRecognizer()
      ..onTap = () {
        print('Tapped!');
      }),
  ],
),

2. 保存和加载布局

你可以通过 StackBoardControllersaveLayoutloadLayout 方法来保存和加载布局。

_controller.saveLayout(); // 保存布局
_controller.loadLayout(layout); // 加载布局
回到顶部