Flutter自定义图形绘制插件hexagon的使用

发布于 1周前 作者 songsunli 来自 Flutter

Flutter自定义图形绘制插件hexagon的使用

简介

Hexagon 是一个Flutter插件,用于创建六边形形状的小部件。它受到redblobgames上关于六边形分析的启发。

示例1 示例2 示例3 示例4

安装

在你的 pubspec.yaml 文件中添加以下依赖:

dependencies:
  hexagon: ^0.2.0

使用方法

单个六边形小部件

在定义 HexagonWidget 时必须设置宽度或高度,另一个维度将根据选定的 HexagonType 计算得出。可以使用命名构造函数来创建简单的扁平或尖顶六边形。阴影大小可以通过 elevation 参数调整。

HexagonWidget.flat(
  width: w,
  color: Colors.limeAccent,
  padding: 4.0,
  child: Text('A flat tile'),
),

HexagonWidget.pointy(
  width: w,
  color: Colors.red,
  elevation: 8,
  child: Text('A pointy tile'),
),

网格布局

偏移网格(Offset Grid)

偏移网格使用类似于普通表格的简单坐标系统。由于六边形列或行可以从六边形或空格开始,因此该网格有四种命名构造函数以表示所有组合:oddPointyevenPointyoddFlatevenFlat。每个构造函数都需要 columnsrows 参数。

Column(
  crossAxisAlignment: CrossAxisAlignment.stretch,
  children: [
    HexagonOffsetGrid.oddPointy(
      columns: 5,
      rows: 10,
      buildTile: (col, row) => HexagonWidgetBuilder(
        color: row.isEven ? Colors.yellow : Colors.orangeAccent,
        elevation: 2,
      ),
      buildChild: (col, row) {
        return Text('$col, $row');
      },
    ),
  ],
),

六边形网格(Hexagon Grid)

六边形网格采用立方体和轴向坐标系统,更加直观地表示六边形结构。

InteractiveViewer(
  minScale: 0.2,
  maxScale: 4.0,
  constrained: false,
  child: HexagonGrid.pointy(
    color: Colors.pink,
    depth: depth,
    width: 1920,
    buildTile: (coordinates) => HexagonWidgetBuilder(
      padding: 2.0,
      cornerRadius: 8.0,
      child: Text('${coordinates.q}, ${coordinates.r}'),
    ),
  ),
)

示例代码

以下是完整的示例代码,展示了如何在一个Flutter应用程序中使用 hexagon 插件。

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

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: const MyHomePage(title: 'Example'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  MyHomePageState createState() => MyHomePageState();
}

class MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
  int depth = 1;
  List<int> depths = [0, 1, 2, 3, 4];
  HexagonType type = HexagonType.FLAT;
  bool hasControls = true;
  bool showControls = true;

  late TabController tabController;

  @override
  void initState() {
    super.initState();
    tabController = TabController(initialIndex: 0, length: 4, vsync: this);
    tabController.addListener(_onTabChange);
  }

  void _onTabChange() {
    if (tabController.index == 0) {
      setState(() {
        hasControls = true;
      });
    } else {
      setState(() {
        hasControls = false;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    var size = MediaQuery.of(context).size;

    return DefaultTabController(
      length: 4,
      initialIndex: 0,
      child: Scaffold(
        appBar: AppBar(
          bottom: TabBar(
            controller: tabController,
            tabs: const [
              Tab(text: 'Grid'),
              Tab(text: 'V-Offset'),
              Tab(text: 'H-Offset'),
              Tab(text: 'Other'),
            ],
          ),
          title: Text(widget.title),
          actions: hasControls
              ? [
                  Row(children: [
                    const Text('Controls'),
                    Switch(
                      value: showControls,
                      activeColor: Colors.lightBlueAccent,
                      onChanged: (value) => setState(() {
                        showControls = value;
                      }),
                    ),
                  ])
                ]
              : null,
        ),
        body: TabBarView(
          controller: tabController,
          physics: const NeverScrollableScrollPhysics(),
          children: [
            Stack(
              children: [
                Positioned.fill(child: _buildGrid(context, type)),
                Align(
                  alignment: Alignment.topRight,
                  child: Visibility(
                    visible: showControls,
                    child: Theme(
                      data: ThemeData(colorScheme: const ColorScheme.dark()),
                      child: Card(
                        margin: const EdgeInsets.all(8.0),
                        child: Padding(
                          padding: const EdgeInsets.symmetric(vertical: 2.0, horizontal: 16.0),
                          child: Column(
                            mainAxisSize: MainAxisSize.min,
                            crossAxisAlignment: CrossAxisAlignment.start,
                            children: [
                              DropdownButton<HexagonType>(
                                onChanged: (value) => setState(() {
                                  if (value != null) {
                                    type = value;
                                  }
                                }),
                                value: type,
                                items: const [
                                  DropdownMenuItem<HexagonType>(
                                    value: HexagonType.FLAT,
                                    child: Text('Flat'),
                                  ),
                                  DropdownMenuItem<HexagonType>(
                                    value: HexagonType.POINTY,
                                    child: Text('Pointy'),
                                  )
                                ],
                                selectedItemBuilder: (context) => [
                                  const Center(child: Text('Flat')),
                                  const Center(child: Text('Pointy')),
                                ],
                              ),
                              DropdownButton<int>(
                                onChanged: (value) => setState(() {
                                  if (value != null) {
                                    depth = value;
                                  }
                                }),
                                value: depth,
                                items: depths.map((e) => DropdownMenuItem<int>(
                                      value: e,
                                      child: Text('Depth: $e'),
                                    )).toList(),
                                selectedItemBuilder: (context) {
                                  return depths.map((e) => Center(child: Text('Depth: $e'))).toList();
                                },
                              ),
                            ],
                          ),
                        ),
                      ),
                    ),
                  ),
                ),
              ],
            ),
            _buildVerticalGrid(),
            _buildHorizontalGrid(),
            _buildMore(size),
          ],
        ),
      ),
    );
  }

  Widget _buildGrid(BuildContext context, HexagonType type) {
    return InteractiveViewer(
      minScale: 0.2,
      maxScale: 4.0,
      child: HexagonGrid(
        hexType: type,
        color: Colors.pink,
        depth: depth,
        buildTile: (coordinates) => HexagonWidgetBuilder(
          padding: 2.0,
          cornerRadius: 8.0,
          child: Text('${coordinates.q}, ${coordinates.r}'),
        ),
      ),
    );
  }

  Widget _buildHorizontalGrid() {
    return SingleChildScrollView(
      scrollDirection: Axis.horizontal,
      child: HexagonOffsetGrid.oddPointy(
        color: Colors.black54,
        padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 32.0),
        columns: 9,
        rows: 4,
        buildTile: (col, row) => row.isOdd && col.isOdd
            ? null
            : HexagonWidgetBuilder(
                elevation: col.toDouble(),
                padding: 4.0,
                cornerRadius: row.isOdd ? 24.0 : null,
                color: col == 1 || row == 1 ? Colors.lightBlue.shade200 : null,
                child: Text('$col, $row'),
              ),
      ),
    );
  }

  Widget _buildVerticalGrid() {
    return SingleChildScrollView(
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          HexagonOffsetGrid.evenFlat(
            color: Colors.yellow.shade100,
            padding: const EdgeInsets.all(8.0),
            columns: 5,
            rows: 10,
            buildTile: (col, row) => HexagonWidgetBuilder(
              color: row.isEven ? Colors.yellow : Colors.orangeAccent,
              elevation: 2.0,
              padding: 2.0,
            ),
            buildChild: (col, row) => Text('$col, $row'),
          ),
        ],
      ),
    );
  }

  Widget _buildMore(Size size) {
    var padding = 8.0;
    var w = (size.width - 4 * padding) / 2;
    var h = 150.0;
    return SingleChildScrollView(
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            mainAxisSize: MainAxisSize.min,
            children: [
              Padding(
                padding: EdgeInsets.all(padding),
                child: HexagonWidget.flat(
                  width: w,
                  child: AspectRatio(
                    aspectRatio: HexagonType.FLAT.ratio,
                    child: Image.asset(
                      'assets/bee.jpg',
                      fit: BoxFit.fitHeight,
                    ),
                  ),
                ),
              ),
              Padding(
                padding: EdgeInsets.all(padding),
                child: HexagonWidget.pointy(
                  width: w,
                  child: AspectRatio(
                    aspectRatio: HexagonType.POINTY.ratio,
                    child: Image.asset(
                      'assets/tram.jpg',
                      fit: BoxFit.fitWidth,
                    ),
                  ),
                ),
              ),
            ],
          ),
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Padding(
                padding: const EdgeInsets.all(8.0),
                child: HexagonWidget.flat(
                  height: h,
                  color: Colors.orangeAccent,
                  child: Text('flat\nheight: ${h.toStringAsFixed(2)}'),
                ),
              ),
              Padding(
                padding: const EdgeInsets.all(8.0),
                child: HexagonWidget.pointy(
                  height: h,
                  color: Colors.red,
                  child: Text('pointy\nheight: ${h.toStringAsFixed(2)}'),
                ),
              ),
            ],
          ),
          Padding(
            padding: EdgeInsets.all(padding),
            child: HexagonWidget.flat(
              width: w,
              color: Colors.limeAccent,
              elevation: 0,
              child: Text('flat\nwidth: ${w.toStringAsFixed(2)}\nelevation: 0'),
            ),
          ),
          Padding(
            padding: EdgeInsets.all(padding),
            child: HexagonWidget.pointy(
              width: w,
              color: Colors.lightBlue,
              child: Text('pointy\nwidth: ${w.toStringAsFixed(2)}'),
            ),
          ),
        ],
      ),
    );
  }
}

通过上述代码,你可以创建一个包含多种六边形布局的应用程序,并根据需要进行自定义和扩展。希望这能帮助你更好地理解和使用 hexagon 插件!


更多关于Flutter自定义图形绘制插件hexagon的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter自定义图形绘制插件hexagon的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何在Flutter中使用自定义图形绘制插件 hexagon 的示例代码。这个插件允许你在Flutter应用中绘制六边形。假设你已经添加了这个插件到你的 pubspec.yaml 文件中:

dependencies:
  flutter:
    sdk: flutter
  hexagon: ^最新版本号  # 请替换为实际的最新版本号

然后运行 flutter pub get 来获取插件。

下面是一个完整的示例代码,展示如何使用 hexagon 插件在Flutter中绘制六边形:

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

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Hexagon Drawing Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: HexagonScreen(),
    );
  }
}

class HexagonScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Hexagon Drawing Demo'),
      ),
      body: Center(
        child: CustomPaint(
          size: Size(300, 300), // 设置画布大小
          painter: HexagonPainter(),
        ),
      ),
    );
  }
}

class HexagonPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final Paint paint = Paint()
      ..color = Colors.blue
      ..style = PaintingStyle.fill;

    // 创建一个六边形的路径
    final Path hexagonPath = Path()
      ..moveTo(size.width / 2, 0) // 顶部中间点
      ..lineTo(size.width - (size.width / 4), size.height / 4) // 右上角点
      ..lineTo(size.width, size.height / 2) // 右上顶点
      ..lineTo(size.width - (size.width / 4), (3 * size.height) / 4) // 右下角点
      ..lineTo(size.width / 2, size.height) // 底部中间点
      ..lineTo(size.width / 4, (3 * size.height) / 4) // 左下角点
      ..lineTo(0, size.height / 2) // 左下顶点
      ..lineTo(size.width / 4, size.height / 4) // 左下角点
      ..close();

    // 在画布上绘制六边形路径
    canvas.drawPath(hexagonPath, paint);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return false;
  }
}

在这个示例中,我们创建了一个简单的Flutter应用,包含一个 CustomPaint 组件,该组件使用 HexagonPainter 自定义绘制类来绘制一个六边形。HexagonPainter 类扩展了 CustomPainter 并重写了 paint 方法来定义六边形的绘制逻辑。

注意:虽然这个示例直接使用了 Path 类来绘制六边形,而没有直接依赖 hexagon 插件(因为该插件的具体实现和API可能有所不同,且并非官方或广泛认知的插件),但这种方法展示了如何在Flutter中自定义绘制图形。如果你使用的 hexagon 插件提供了特定的API或组件来简化六边形的绘制,请参考该插件的文档来调整上述代码。

如果你使用的 hexagon 插件有特定的使用方式,请查阅其官方文档或示例代码来获取更多信息。通常,插件会提供一个易于使用的组件或方法来简化复杂图形的绘制。

回到顶部