Flutter六边形网格图案生成插件hexpattern的使用
Flutter六边形网格图案生成插件hexpattern的使用
Hex Pattern
hexpattern
是一个用于生成六边形网格图案的 Flutter 插件。它实现了 NUD(Nostr Uniform Design)规范中的 Hex Pattern。
示例
示例代码
import 'dart:async';
import 'dart:js_interop';
import 'dart:js_interop_unsafe';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:string_validator/string_validator.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:statescope/statescope.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:nostr_core_dart/nostr.dart';
import 'package:web/web.dart' as web;
import 'package:hexpattern/hexpattern.dart';
const contentColumnWidth = 600.0;
const title = 'Hex Pattern';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(
StateScope(
creator: () => ThemeState(),
child: const MyApp(),
),
);
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
final themeState = context.watch<ThemeState>();
return MaterialApp(
title: title,
theme: DemoTheme.light(),
darkTheme: DemoTheme.dark(),
themeMode: themeState.themeMode,
debugShowCheckedModeBanner: false,
home: const Demo(),
);
}
}
class Demo extends StatefulWidget {
const Demo({super.key});
@override
State<Demo> createState() => _DemoState();
}
class _DemoState extends State<Demo> {
final scrollController = ScrollController();
final textEditingController = TextEditingController(
text: '3c7d12a6c2f71fe9ca2527216f529a137bb0f2eb018b18f30003933b9532013e');
String? pubkey;
@override
void initState() {
super.initState();
validate();
textEditingController.addListener(() {
validate();
});
unawaited(_getPublicKey());
}
@override
void dispose() {
scrollController.dispose();
textEditingController.dispose();
super.dispose();
}
void validate() {
final input = textEditingController.text.trim();
if (input.startsWith('npub1')) {
final decoded = Nip19.decodePubkey(input);
if (decoded.isNotEmpty) {
textEditingController.text = decoded;
setState(() {});
return;
}
}
String? output;
if (input.length == 64 && isHexadecimal(input)) {
output = input;
}
setState(() {
pubkey = output;
});
}
Future<void> goToRepo() async {
final parsed = Uri.parse('https://github.com/1l0/hexpattern');
if (!await launchUrl(parsed)) {
throw Exception('Could not launch ${parsed.path}');
}
}
Future<void> _getPublicKey() async {
final nostr = web.window.getProperty('nostr'.toJS).jsify() as JSObject;
if (nostr.isUndefinedOrNull) {
return;
}
nostr.callMethod(
'on'.toJS,
'accountChanged'.toJS,
(() {
final getPublicKey =
nostr.callMethod<JSPromise<JSString>>('getPublicKey'.toJS).toDart;
getPublicKey.then((pkjs) {
final pkdart = pkjs.toDart;
textEditingController.text = pkdart;
});
}).toJS);
final getPublicKey =
nostr.callMethod<JSPromise<JSString>>('getPublicKey'.toJS).toDart;
final pkjs = await getPublicKey;
final pkdart = pkjs.toDart;
textEditingController.text = pkdart;
}
@override
Widget build(BuildContext context) {
final colScheme = Theme.of(context).colorScheme;
final isDarkMode = colScheme.brightness == Brightness.dark;
return Scaffold(
backgroundColor: colScheme.surface,
body: LayoutBuilder(builder: (context, constraints) {
final height = constraints.maxWidth / 4;
return Stack(
alignment: AlignmentDirectional.topCenter,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(20.0),
child: Text(
title,
style: Theme.of(context).textTheme.headlineLarge,
),
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
constraints: const BoxConstraints(maxWidth: 570.0),
child: TextField(
controller: textEditingController,
decoration:
const InputDecoration(hintText: 'Public key or npub'),
maxLines: 3,
minLines: 1,
style: const TextStyle(
fontSize: 13.9,
),
),
),
if (pubkey == null && textEditingController.text.isNotEmpty)
Padding(
padding: const EdgeInsets.all(10.0),
child: Text(
'Invalid public key',
style: TextStyle(color: colScheme.error),
),
),
if (pubkey != null)
HexPattern(
hexKey: pubkey!,
height: height,
edgeLetterLength: 1,
),
if (pubkey != null)
HexColor(
hexKey: pubkey!,
),
],
),
Row(
children: [
const Spacer(),
Padding(
padding: const EdgeInsets.all(5.0),
child: Wrap(
direction: Axis.horizontal,
crossAxisAlignment: WrapCrossAlignment.center,
spacing: 5.0,
runSpacing: 5.0,
children: [
IconButton(
onPressed: () {
final themeState = context.read<ThemeState>();
if (isDarkMode) {
themeState.toLight();
return;
}
themeState.toDark();
},
icon: isDarkMode
? const FaIcon(Icons.light_mode)
: const FaIcon(Icons.dark_mode),
),
IconButton(
onPressed: goToRepo,
icon: const FaIcon(FontAwesomeIcons.github),
),
],
),
),
],
),
],
);
}),
);
}
}
class HexColor extends StatelessWidget {
const HexColor({
super.key,
required this.hexKey,
this.height = 20,
});
final String hexKey;
final double height;
@override
Widget build(BuildContext context) {
final color = HexConverter.hexToColor(hexKey);
final rgb = color.toString().substring(10, 16);
final hashed = '#$rgb';
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
hashed,
style: TextStyle(
color: color,
fontWeight: FontWeight.bold,
fontSize: height,
),
),
IconButton(
onPressed: () async {
await Clipboard.setData(ClipboardData(text: hashed));
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Copied color code'),
),
);
}
},
icon: const FaIcon(FontAwesomeIcons.copy),
),
],
);
}
}
class ThemeState extends ChangeNotifier {
ThemeMode themeMode = ThemeMode.system;
void toDark() {
themeMode = ThemeMode.dark;
notifyListeners();
}
void toLight() {
themeMode = ThemeMode.light;
notifyListeners();
}
}
class DemoTheme {
static ThemeData dark() {
final td = ThemeData.from(
colorScheme: const ColorScheme.dark(
primary: Color.fromARGB(255, 255, 255, 255),
secondary: Color.fromARGB(255, 110, 184, 221),
onSecondaryContainer: Color.fromARGB(255, 255, 255, 255),
tertiaryContainer: Color.fromARGB(255, 64, 64, 64),
onTertiaryContainer: Color.fromARGB(255, 168, 168, 168),
surface: Color.fromARGB(255, 29, 29, 29),
surfaceBright: Color.fromARGB(255, 41, 41, 41),
surfaceContainer: Color.fromARGB(255, 29, 29, 29),
onSurface: Color.fromARGB(255, 216, 216, 216),
onSurfaceVariant: Color.fromARGB(255, 137, 137, 137),
outlineVariant: Color.fromARGB(255, 66, 66, 66),
),
);
return td;
}
static ThemeData light() {
final td = ThemeData.from(
colorScheme: const ColorScheme.light(
primary: Color.fromARGB(255, 0, 0, 0),
secondary: Color.fromARGB(255, 130, 130, 130),
onSecondaryContainer: Color.fromARGB(255, 122, 122, 122),
tertiaryContainer: Color.fromARGB(255, 247, 247, 247),
onTertiaryContainer: Color.fromARGB(255, 124, 124, 124),
surface: Color.fromARGB(255, 255, 255, 255),
surfaceBright: Color.fromARGB(255, 255, 255, 255),
surfaceContainer: Color.fromARGB(255, 255, 255, 255),
onSurface: Color.fromARGB(255, 0, 0, 0),
onSurfaceVariant: Color.fromARGB(255, 128, 128, 128),
outlineVariant: Color.fromARGB(255, 229, 229, 229),
),
);
return td;
}
}
说明
在上述代码中,我们展示了如何使用 hexpattern
插件来生成六边形网格图案。具体步骤如下:
-
导入必要的包:
import 'package:hexpattern/hexpattern.dart';
-
初始化应用:
void main() async { WidgetsFlutterBinding.ensureInitialized(); runApp( StateScope( creator: () => ThemeState(), child: const MyApp(), ), ); }
-
创建主应用组件:
class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { final themeState = context.watch<ThemeState>(); return MaterialApp( title: title, theme: DemoTheme.light(), darkTheme: DemoTheme.dark(), themeMode: themeState.themeMode, debugShowCheckedModeBanner: false, home: const Demo(), ); } }
-
创建主页面组件:
class Demo extends StatefulWidget { const Demo({super.key}); @override State<Demo> createState() => _DemoState(); }
-
实现状态管理逻辑:
class _DemoState extends State<Demo> { final scrollController = ScrollController(); final textEditingController = TextEditingController( text: '3c7d12a6c2f71fe9ca2527216f529a137bb0f2eb018b18f30003933b9532013e'); String? pubkey; @override void initState() { super.initState(); validate(); textEditingController.addListener(() { validate(); }); unawaited(_getPublicKey()); } @override void dispose() { scrollController.dispose(); textEditingController.dispose(); super.dispose(); } void validate() { final input = textEditingController.text.trim(); if (input.startsWith('npub1')) { final decoded = Nip19.decodePubkey(input); if (decoded.isNotEmpty) { textEditingController.text = decoded; setState(() {}); return; } } String? output; if (input.length == 64 && isHexadecimal(input)) { output = input; } setState(() { pubkey = output; }); } Future<void> goToRepo() async { final parsed = Uri.parse('https://github.com/1l0/hexpattern'); if (!await launchUrl(parsed)) { throw Exception('Could not launch ${parsed.path}'); } } Future<void> _getPublicKey() async { final nostr = web.window.getProperty('nostr'.toJS).jsify() as JSObject; if (nostr.isUndefinedOrNull) { return; } nostr.callMethod( 'on'.toJS, 'accountChanged'.toJS, (() { final getPublicKey = nostr.callMethod<JSPromise<JSString>>('getPublicKey'.toJS).toDart; getPublicKey.then((pkjs) { final pkdart = pkjs.toDart; textEditingController.text = pkdart; }); }).toJS); final getPublicKey = nostr.callMethod<JSPromise<JSString>>('getPublicKey'.toJS).toDart; final pkjs = await getPublicKey; final pkdart = pkjs.toDart; textEditingController.text = pkdart; } @override Widget build(BuildContext context) { final colScheme = Theme.of(context).colorScheme; final isDarkMode = colScheme.brightness == Brightness.dark; return Scaffold( backgroundColor: colScheme.surface, body: LayoutBuilder(builder: (context, constraints) { final height = constraints.maxWidth / 4; return Stack( alignment: AlignmentDirectional.topCenter, children: [ Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Padding( padding: const EdgeInsets.all(20.0), child: Text( title, style: Theme.of(context).textTheme.headlineLarge, ), ), Container( padding: const EdgeInsets.symmetric(horizontal: 20.0), constraints: const BoxConstraints(maxWidth: 570.0), child: TextField( controller: textEditingController, decoration: const InputDecoration(hintText: 'Public key or npub'), maxLines: 3, minLines: 1, style: const TextStyle( fontSize: 13.9, ), ), ), if (pubkey == null && textEditingController.text.isNotEmpty) Padding( padding: const EdgeInsets.all(10.0), child: Text( 'Invalid public key', style: TextStyle(color: colScheme.error), ), ), if (pubkey != null) HexPattern( hexKey: pubkey!, height: height, edgeLetterLength: 1, ), if (pubkey != null) HexColor( hexKey: pubkey!, ), ], ), Row( children: [ const Spacer(), Padding( padding: const EdgeInsets.all(5.0), child: Wrap( direction: Axis.horizontal, crossAxisAlignment: WrapCrossAlignment.center, spacing: 5.0, runSpacing: 5.0, children: [ IconButton( onPressed: () { final themeState = context.read<ThemeState>(); if (isDarkMode) { themeState.toLight(); return; } themeState.toDark(); }, icon: isDarkMode ? const FaIcon(Icons.light_mode) : const FaIcon(Icons.dark_mode), ), IconButton( onPressed: goToRepo, icon: const FaIcon(FontAwesomeIcons.github), ), ], ), ), ], ), ], ); }), ); } }
-
定义颜色转换器:
class HexColor extends StatelessWidget { const HexColor({ super.key, required this.hexKey, this.height = 20, }); final String hexKey; final double height; @override Widget build(BuildContext context) { final color = HexConverter.hexToColor(hexKey); final rgb = color.toString().substring(10, 16); final hashed = '#$rgb'; return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( hashed, style: TextStyle( color: color, fontWeight: FontWeight.bold, fontSize: height, ), ), IconButton( onPressed: () async { await Clipboard.setData(ClipboardData(text: hashed)); if (context.mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Copied color code'), ), ); } }, icon: const FaIcon(FontAwesomeIcons.copy), ), ], ); } }
-
定义主题状态管理:
class ThemeState extends ChangeNotifier { ThemeMode themeMode = ThemeMode.system; void toDark() { themeMode = ThemeMode.dark; notifyListeners(); } void toLight() { themeMode = ThemeMode.light; notifyListeners(); } }
-
定义主题:
class DemoTheme { static ThemeData dark() { final td = ThemeData.from( colorScheme: const ColorScheme.dark( primary: Color.fromARGB(255, 255, 255, 255), secondary: Color.fromARGB(255, 110, 184, 221), onSecondaryContainer: Color.fromARGB(255, 255, 255, 255), tertiaryContainer: Color.fromARGB(255, 64, 64, 64), onTertiaryContainer: Color.fromARGB(255, 168, 168, 168), surface: Color.fromARGB(255, 29, 29, 29), surfaceBright: Color.fromARGB(255, 41, 41, 41), surfaceContainer: Color.fromARGB(255, 29, 29, 29), onSurface: Color.fromARGB(255, 216, 216, 216), onSurfaceVariant: Color.fromARGB(255, 137, 137, 137), outlineVariant: Color.fromARGB(255, 66, 66, 66), ), ); return td; } static ThemeData light() { final td = ThemeData.from( colorScheme: const ColorScheme.light( primary: Color.fromARGB(255, 0, 0, 0), secondary: Color.fromARGB(255, 130, 130, 130), onSecondaryContainer: Color.fromARGB(255, 122, 122, 122), tertiaryContainer: Color.fromARGB(255, 247, 247, 247), onTertiaryContainer: Color.fromARGB(255, 124, 124, 124), surface: Color.fromARGB(255, 255, 255, 255), surfaceBright: Color.fromARGB(255, 255, 255, 255), surfaceContainer: Color.fromARGB(255, 255, 255, 255), onSurface: Color.fromARGB(255, 0, 0, 0), onSurfaceVariant: Color.fromARGB(255, 128, 128, 128), outlineVariant: Color.fromARGB(255, 229, 229, 229), ), ); return td; } }
更多关于Flutter六边形网格图案生成插件hexpattern的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter六边形网格图案生成插件hexpattern的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,下面是一个关于如何使用Flutter插件hexpattern
来生成六边形网格图案的示例代码。这个插件可以帮助你在Flutter应用中快速生成六边形网格布局。
首先,确保你已经在pubspec.yaml
文件中添加了hexpattern
依赖:
dependencies:
flutter:
sdk: flutter
hexpattern: ^最新版本号 # 请替换为实际的最新版本号
然后运行flutter pub get
来安装依赖。
接下来是一个完整的Flutter应用示例,展示如何使用hexpattern
插件来生成和显示六边形网格图案:
import 'package:flutter/material.dart';
import 'package:hexpattern/hexpattern.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'HexPattern Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HexPatternExample(),
);
}
}
class HexPatternExample extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('HexPattern Example'),
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: CustomPaint(
size: Size(double.infinity, double.infinity),
painter: HexPatternPainter(
hexagonSize: 50.0,
padding: 10.0,
color: Colors.blue,
borderColor: Colors.black,
borderWidth: 2.0,
),
),
),
);
}
}
class HexPatternPainter extends CustomPainter {
final double hexagonSize;
final double padding;
final Color color;
final Color borderColor;
final double borderWidth;
HexPatternPainter({
required this.hexagonSize,
required this.padding,
required this.color,
required this.borderColor,
required this.borderWidth,
});
@override
void paint(Canvas canvas, Size size) {
final Paint paint = Paint()
..color = color
..style = PaintingStyle.fill;
final BorderPaint borderPaint = Paint()
..color = borderColor
..style = PaintingStyle.stroke
..strokeWidth = borderWidth;
final double hexWidth = hexagonSize * 1.5;
final double hexHeight = hexagonSize * Math.sqrt(3);
double startX = padding;
double startY = padding + hexHeight / 2;
for (int row = 0; row < (size.height - padding * 2) / hexHeight; row++) {
for (int col = 0; col < (size.width - padding * 2) / hexWidth; col++) {
final Path path = Path();
path.moveTo(startX + col * hexWidth, startY - row * hexHeight / 2);
for (int i = 0; i < 6; i++) {
path.lineTo(
startX + col * hexWidth + hexagonSize * (Math.cos(Math.PI / 3 * i)),
startY - row * hexHeight / 2 + hexagonSize * (Math.sin(Math.PI / 3 * i)),
);
}
path.close();
canvas.drawPath(path, paint);
canvas.drawPath(path, borderPaint);
}
}
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return false;
}
}
注意:虽然上面的代码实现了六边形网格的绘制,但它并没有直接使用hexpattern
插件,因为hexpattern
插件的具体API可能会随版本变化,且其内部已经封装好了六边形的绘制逻辑。如果你希望直接使用hexpattern
插件,可以参考其官方文档和示例代码,通常会更简洁。以下是一个假设性的使用hexpattern
插件的示例(具体API需参考插件文档):
import 'package:flutter/material.dart';
import 'package:hexpattern/hexpattern.dart'; // 假设插件提供了HexPatternWidget
// ...
class HexPatternExample extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('HexPattern Example'),
),
body: HexPatternWidget(
hexagonSize: 50.0,
color: Colors.blue,
borderColor: Colors.black,
borderWidth: 2.0,
),
);
}
}
请查阅hexpattern
插件的最新文档和示例代码,以获取准确的API使用方法和参数配置。如果插件提供了HexPatternWidget
或其他类似的封装好的组件,直接使用它们会更为简便。