diff --git a/lib/base_page.dart b/lib/base_page.dart index 40fc1f3..2932eb8 100644 --- a/lib/base_page.dart +++ b/lib/base_page.dart @@ -1,154 +1,156 @@ import 'dart:ui'; import 'package:flutter/material.dart'; -import 'package:refreshed/refreshed.dart'; +import 'package:go_router/go_router.dart'; import 'package:simple_icons/simple_icons.dart'; import 'package:tools/func_enum.dart'; import 'package:tools/global_variable.dart'; import 'package:web/web.dart' as web; -// class Man - class BasePage extends StatelessWidget { final Future Function() child; final String title; - final Rxn body = Rxn(); - BasePage({super.key, required this.title, required this.child}); + // final Rxn body = Rxn(); + const BasePage({super.key, required this.title, required this.child}); // bool get isDark => Theme.of(Get.context!).brightness == Brightness.dark; @override Widget build(BuildContext context) { - if (body.value == null) child().then(body.call); - bool isDark = Theme.of(context).brightness == Brightness.dark; return Scaffold( - appBar: AppBar( - title: Text(title), - centerTitle: true, - elevation: 0, - shadowColor: Colors.transparent, - backgroundColor: Colors.transparent, - scrolledUnderElevation: 0, - ), - drawer: Drawer( - child: Column( - children: [ - DrawerHeader( - decoration: const BoxDecoration( - color: Color.fromARGB(255, 44, 78, 105), - ), - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - const Text( - '功能列表', - style: TextStyle(color: Colors.white, fontSize: 24), - ), + appBar: AppBar( + title: Text(title), + centerTitle: true, + elevation: 0, + shadowColor: Colors.transparent, + backgroundColor: Colors.transparent, + scrolledUnderElevation: 0, + ), + drawer: Drawer( + child: Column( + children: [ + DrawerHeader( + decoration: const BoxDecoration( + color: Color.fromARGB(255, 44, 78, 105), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + const Text( + '功能列表', + style: TextStyle(color: Colors.white, fontSize: 24), + ), - /// 搜索框 - ClipRRect( - borderRadius: BorderRadius.circular(20), - child: BackdropFilter( - filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10), - child: Container( - decoration: BoxDecoration( - color: Colors.white.withAlpha(100), - borderRadius: BorderRadius.circular(20), - border: - Border.all(color: Colors.white.withAlpha(100)), - ), - child: TextField( - onChanged: GlobalVariable.searchRx.call, - controller: TextEditingController( - text: GlobalVariable.searchRx.value), - decoration: const InputDecoration( - hintText: "搜索工具", - prefixIcon: Icon(Icons.search), - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric( - horizontal: 16, vertical: 14), - ), + /// 搜索框 + ClipRRect( + borderRadius: BorderRadius.circular(20), + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10), + child: Container( + decoration: BoxDecoration( + color: Colors.white.withAlpha(100), + borderRadius: BorderRadius.circular(20), + border: + Border.all(color: Colors.white.withAlpha(100)), + ), + child: TextField( + onChanged: (value) { + GlobalVariable.searchNotifier.value = value; + }, + controller: TextEditingController( + text: GlobalVariable.searchNotifier.value), + decoration: const InputDecoration( + hintText: "搜索工具", + prefixIcon: Icon(Icons.search), + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric( + horizontal: 16, vertical: 14), ), ), ), - ) - ], - ), + ), + ) + ], ), - Expanded( - child: ListView( - /// 内容居中 - physics: const BouncingScrollPhysics(), - padding: EdgeInsets.zero, - children: [ - ValueListenableBuilder( - valueListenable: GlobalVariable.searchRx, - builder: (context, value, child) { - List list = FunctionEnum.values - .where((element) => (GlobalVariable - .searchRx.value.isNotEmpty - ? element.name - .contains(GlobalVariable.searchRx.value) - : true)) - .toList(); - if (list.isEmpty) { - return const ListTile( - title: Text("没有找到相关工具"), - ); - } - - return Column( - children: (list) - .map((e) => ListTile( - title: Text(e.name), - tileColor: e.route == Get.currentRoute - ? Colors.blue.withAlpha(100) - : null, - onTap: e.onTap)) - .toList(), + ), + Expanded( + child: ListView( + /// 内容居中 + physics: const BouncingScrollPhysics(), + padding: EdgeInsets.zero, + children: [ + ValueListenableBuilder( + valueListenable: GlobalVariable.searchNotifier, + builder: (context, value, child) { + List list = FunctionEnum.values + .where((element) => + (GlobalVariable.searchNotifier.value.isNotEmpty + ? element.name.contains( + GlobalVariable.searchNotifier.value) + : true)) + .toList(); + if (list.isEmpty) { + return const ListTile( + title: Text("没有找到相关工具"), ); - }), - ], - ), + } + + return Column( + children: (list) + .map((e) => ListTile( + title: Text(e.name), + tileColor: e.route == + GoRouter.of(context).state?.path + ? Colors.blue.withAlpha(100) + : null, + onTap: e.onTap)) + .toList(), + ); + }), + ], ), - const Divider(), - Padding( - padding: const EdgeInsets.all(16.0), - child: Row( - children: [ - TextButton( + ), + const Divider(), + Padding( + padding: const EdgeInsets.all(16.0), + child: Row( + children: [ + TextButton( + onPressed: () { + web.window.open("https://github.com/bymoye/tools"); + }, + child: const Row( + children: [ + Icon(SimpleIcons.github, size: 20), + SizedBox(width: 8), + Text("bymoye/tools") + ], + ), + ), + const Spacer(), + IconButton( onPressed: () { - web.window.open("https://github.com/bymoye/tools"); + GlobalVariable.themeModeNotifier.value = + isDark ? ThemeMode.light : ThemeMode.dark; }, - child: const Row( - children: [ - Icon(SimpleIcons.github, size: 20), - SizedBox(width: 8), - Text("bymoye/tools") - ], - ), - ), - const Spacer(), - IconButton( - onPressed: () { - Get.changeThemeMode( - isDark ? ThemeMode.light : ThemeMode.dark); - }, - icon: - Icon(isDark ? Icons.light_mode : Icons.dark_mode)) - ], - )) - ], - ), + icon: Icon(isDark ? Icons.light_mode : Icons.dark_mode)) + ], + )) + ], ), - body: Obx( - () => - body.value ?? - const Center( - child: CircularProgressIndicator(), - ), - )); + ), + body: FutureBuilder( + future: child(), + builder: (context, snapshot) { + return snapshot.hasData + ? snapshot.data! + : const Center( + child: CircularProgressIndicator(), + ); + }, + ), + ); } } diff --git a/lib/func_enum.dart b/lib/func_enum.dart index f2bd40d..9063cbf 100644 --- a/lib/func_enum.dart +++ b/lib/func_enum.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:refreshed/refreshed.dart'; +import 'package:tools/router.dart'; import 'package:web/web.dart' as web; /// 首页 @@ -91,7 +91,7 @@ enum FunctionEnum { web.window.open("https://squoosh.app/", "squoosh"); break; default: - Get.offAllNamed(route); + router.go(route); } } } diff --git a/lib/global_variable.dart b/lib/global_variable.dart index 8823846..26ab2de 100644 --- a/lib/global_variable.dart +++ b/lib/global_variable.dart @@ -1,5 +1,7 @@ -import 'package:refreshed/refreshed.dart'; +import 'package:flutter/material.dart'; class GlobalVariable { - static RxString searchRx = RxString(""); + static ValueNotifier themeModeNotifier = + ValueNotifier(ThemeMode.system); + static ValueNotifier searchNotifier = ValueNotifier(""); } diff --git a/lib/json_tools/json_utils.dart b/lib/json_tools/json_utils.dart index 893e010..da32581 100644 --- a/lib/json_tools/json_utils.dart +++ b/lib/json_tools/json_utils.dart @@ -1,18 +1,19 @@ import 'dart:convert'; import 'package:flutter/material.dart'; -import 'package:refreshed/refreshed.dart'; -class JsonUtilsPage extends StatelessWidget { - JsonUtilsPage({super.key}); +class JsonUtilsPage extends StatefulWidget { + const JsonUtilsPage({super.key}); - final TextEditingController _controller = TextEditingController(); + @override + State createState() => _JsonUtilsPageState(); +} - /// 输出框 - final RxString _output = RxString(""); +class _JsonUtilsPageState extends State { + final TextEditingController _controller = TextEditingController(); + final TextEditingController _outputController = TextEditingController(); - /// 状态提示 - final RxString _status = RxString(""); + String _status = ""; @override Widget build(BuildContext context) { @@ -38,10 +39,14 @@ class JsonUtilsPage extends StatelessWidget { /// 将输入框的内容格式化, 缩进2个空格 try { var json = jsonDecode(_controller.text); - _output.value = + _outputController.text = const JsonEncoder.withIndent(" ").convert(json); + _status = ""; + setState(() {}); } catch (e) { - _output.value = "JSON格式错误"; + setState(() { + _status = "JSON格式错误"; + }); } }, child: const Text("格式化"), @@ -50,25 +55,29 @@ class JsonUtilsPage extends StatelessWidget { onPressed: () { try { var json = jsonDecode(_controller.text); - _output.value = jsonEncode(json); + _outputController.text = jsonEncode(json); + _status = ""; + setState(() {}); } catch (e) { - _output.value = "JSON格式错误"; + setState(() { + _status = "JSON格式错误"; + }); } }, child: const Text("压缩"), ), ], ), - Obx(() => Text(_status.value)), + Text(_status), const SizedBox(height: 30), const Text("结果", style: TextStyle(fontSize: 15)), - Obx(() => TextField( - controller: TextEditingController(text: _output.value), - readOnly: true, - maxLines: 10, + TextField( + controller: _outputController, + readOnly: true, + maxLines: 10, - /// 高度可被拉伸 - )) + /// 高度可被拉伸 + ) ], ), ); diff --git a/lib/main.dart b/lib/main.dart index 4964262..3bb78e4 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,8 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; -import 'package:refreshed/refreshed.dart'; -import 'package:tools/base_page.dart'; -import 'package:tools/func_enum.dart'; +import 'package:tools/global_variable.dart'; +import 'package:tools/router.dart'; void main() { WidgetsFlutterBinding.ensureInitialized(); @@ -15,31 +14,26 @@ class MainApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { - return GetMaterialApp( - title: 'Tools for Flutter', - theme: ThemeData.light(useMaterial3: true), - darkTheme: ThemeData.dark(), - supportedLocales: const [ - Locale('zh', 'CN'), - Locale('en', 'US'), - ], - getPages: FunctionEnum.values - .where((element) => element.route.isNotEmpty) - .map( - (e) => GetPage( - name: e.route, - page: () => BasePage(title: e.name, child: e.widget), - ), - ) - .toList(), - - localizationsDelegates: const [ - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - ], - // themeMode: themeMode, - initialRoute: '/', + return ValueListenableBuilder( + valueListenable: GlobalVariable.themeModeNotifier, + builder: (_, mode, __) { + return MaterialApp.router( + title: 'Tools for Flutter', + theme: ThemeData.light(useMaterial3: true), + themeMode: mode, + darkTheme: ThemeData.dark(), + supportedLocales: const [ + Locale('zh', 'CN'), + Locale('en', 'US'), + ], + routerConfig: router, + localizationsDelegates: const [ + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + ); + }, ); } } diff --git a/lib/qr_code/parse/controller.dart b/lib/qr_code/parse/controller.dart deleted file mode 100644 index f34271d..0000000 --- a/lib/qr_code/parse/controller.dart +++ /dev/null @@ -1,59 +0,0 @@ -import 'dart:typed_data'; -import 'package:flutter/material.dart'; -import 'package:flutter_dropzone/flutter_dropzone.dart'; -import 'package:refreshed/refreshed.dart'; -import 'package:mime/mime.dart'; -// import 'package:flutter_avif/flutter_avif.dart' deferred as avif; - -class QrCodeParseController extends GetxController { - /// 二维码内容 - RxString qrCode = RxString(""); - - /// 拖放文件控制器 - DropzoneViewController? dropzoneController; - - /// 解析的图片 - final Rx image = Rx(const SizedBox()); - - final RxBool isHover = RxBool(false); - - onHover() { - if (isHover.isTrue) { - return; - } - isHover(true); - } - - parse(dynamic ev) async { - isHover(false); - Uint8List? file = await dropzoneController?.getFileData(ev); - - if (file == null) { - return; - } - - // 判断文件类型 - String mimeType = lookupMimeType( - await dropzoneController?.getFilename(ev) ?? '', - headerBytes: file) ?? - ""; - - /// 如果不是图片文件 - if (!mimeType.startsWith("image/")) { - qrCode.value = "传入的文件不是图片"; - return; - } - // 判断是否是avif格式 - // if (mimeType == "image/avif") { - // // 加载avif库 - // await avif.loadLibrary(); - // // 解码avif图片 - // image.value = avif.AvifImage.memory(file); - // } else { - // 解码普通图片 - image.value = Image.memory(file); - // } - qrCode.value = "解析中..."; - // qrCode.value = await QrCodeTools.parse(file); - } -} diff --git a/lib/qr_code/parse/index.dart b/lib/qr_code/parse/index.dart index e9660f3..242a532 100644 --- a/lib/qr_code/parse/index.dart +++ b/lib/qr_code/parse/index.dart @@ -1,9 +1,7 @@ import 'dart:ui'; import 'package:flutter/material.dart'; -import 'package:refreshed/refreshed.dart'; import 'package:flutter_dropzone/flutter_dropzone.dart'; -import 'package:tools/qr_code/parse/controller.dart'; import 'package:dotted_border/dotted_border.dart'; class QrCodeParsePage extends StatelessWidget { @@ -35,32 +33,33 @@ class QrCodeParsePage extends StatelessWidget { @override Widget build(BuildContext context) { - return GetBuilder( - init: QrCodeParseController(), - builder: (QrCodeParseController controller) { - /// 功能: 1. 拖放文件解析二维码 - return Center( - child: Stack( - children: [ - DropzoneView( - operation: DragOperation.copy, - cursor: CursorType.Default, - onCreated: (ctrl) => controller.dropzoneController = ctrl, - onDrop: controller.parse, - onHover: controller.onHover, - onLeave: () { - controller.isHover(false); - }, - ), - const Text("hello"), - const SizedBox(height: 20), - Text(controller.qrCode.value), - Obx(() => controller.image.value), - Obx(() => controller.isHover.isTrue ? mask() : const SizedBox()), - ], - ), - ); - }, - ); + return const Text("hello"); + // return GetBuilder( + // init: QrCodeParseController(), + // builder: (QrCodeParseController controller) { + // /// 功能: 1. 拖放文件解析二维码 + // return Center( + // child: Stack( + // children: [ + // DropzoneView( + // operation: DragOperation.copy, + // cursor: CursorType.Default, + // onCreated: (ctrl) => controller.dropzoneController = ctrl, + // onDrop: controller.parse, + // onHover: controller.onHover, + // onLeave: () { + // controller.isHover(false); + // }, + // ), + // const Text("hello"), + // const SizedBox(height: 20), + // Text(controller.qrCode.value), + // Obx(() => controller.image.value), + // Obx(() => controller.isHover.isTrue ? mask() : const SizedBox()), + // ], + // ), + // ); + // }, + // ); } } diff --git a/lib/router.dart b/lib/router.dart new file mode 100644 index 0000000..3aa3d4b --- /dev/null +++ b/lib/router.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:tools/base_page.dart'; +import 'package:tools/func_enum.dart'; +import 'package:web/web.dart' as web; + +final GoRouter router = GoRouter( + routes: FunctionEnum.values + .where((element) => element.route.isNotEmpty) + .map( + (e) => GoRoute( + path: e.route, + pageBuilder: (context, state) { + web.document.title = e.name; + return MaterialPage( + key: state.pageKey, + child: BasePage(title: e.name, child: e.widget), + ); + }, + ), + ) + .toList(), +); diff --git a/lib/uuid_tools/generate.dart b/lib/uuid_tools/generate.dart index a5fdd97..616f8fe 100644 --- a/lib/uuid_tools/generate.dart +++ b/lib/uuid_tools/generate.dart @@ -1,4 +1,10 @@ +import 'dart:js_interop'; +import 'dart:math'; + import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:uuid/uuid.dart'; +import 'package:web/web.dart' as web; class UUuidGeneratePage extends StatefulWidget { const UUuidGeneratePage({super.key}); @@ -7,9 +13,380 @@ class UUuidGeneratePage extends StatefulWidget { State createState() => _UUuidGeneratePaageState(); } +enum UuidVersion { v1, v4, v5, v6, v7, v8 } + class _UUuidGeneratePaageState extends State { + final Uuid uuid = const Uuid(); + + static const List namespaces = [ + Namespace.dns, + Namespace.url, + Namespace.oid, + Namespace.x500, + Namespace.nil + ]; + Namespace selectNamespace = Namespace.dns; + + final TextEditingController v5NametextEditingController = + TextEditingController(); + + /// 生成数量 + final TextEditingController generateCountTextEditingController = + TextEditingController(text: '1'); + + Map get uuidVersions => { + UuidVersion.v1: () => uuid.v1(), + UuidVersion.v4: () => uuid.v4(), + UuidVersion.v5: () => + uuid.v5(selectNamespace.value, v5NametextEditingController.text), + UuidVersion.v6: () => uuid.v6(), + UuidVersion.v7: () => uuid.v7(), + UuidVersion.v8: () => uuid.v8(), + }; + + UuidVersion selectUuidVersion = UuidVersion.v1; + + /// 生成后的列表 + // String uuidResult = ''; + List uuidResult = []; + + String get uuidDisplay => uuidResult.join('\n'); + + /// 生成参数 + Widget _buildGenerateParams() { + return Card( + elevation: 8, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + color: Theme.of(context).brightness == Brightness.dark + ? Colors.grey[850] + : Colors.white, + child: Padding( + padding: const EdgeInsets.all(24.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + /// UUID版本选择 + Row( + children: [ + SizedBox( + width: 100, + child: Text( + 'UUID版本:', + style: TextStyle( + fontWeight: FontWeight.bold, + color: Theme.of(context).textTheme.bodyMedium!.color, + ), + textAlign: TextAlign.right, + ), + ), + Expanded( + child: DropdownButtonHideUnderline( + child: Container( + decoration: BoxDecoration( + border: Border.all( + color: Theme.of(context).dividerColor, + ), + borderRadius: BorderRadius.circular(5), + ), + child: DropdownButton( + isExpanded: true, + padding: const EdgeInsets.symmetric(horizontal: 20), + value: selectUuidVersion, + items: UuidVersion.values + .map( + (e) => DropdownMenuItem( + value: e, + child: Text(e.name), + ), + ) + .toList(), + onChanged: (UuidVersion? value) { + if (value == null) return; + setState(() { + selectUuidVersion = value; + }); + }, + ), + ), + ), + ) + ], + ), + + /// UUID版本为v5时的命名空间和名称 + if (selectUuidVersion == UuidVersion.v5) ...[ + Padding( + padding: const EdgeInsets.symmetric(vertical: 20), + child: Row( + children: [ + SizedBox( + width: 100, + child: Text( + '命名空间:', + style: TextStyle( + fontWeight: FontWeight.bold, + color: Theme.of(context).textTheme.bodyMedium!.color, + ), + textAlign: TextAlign.right, + ), + ), + Expanded( + child: DropdownButtonHideUnderline( + child: Container( + decoration: BoxDecoration( + border: Border.all( + color: Theme.of(context).dividerColor, + ), + borderRadius: BorderRadius.circular(5), + ), + child: DropdownButton( + isExpanded: true, + padding: const EdgeInsets.symmetric(horizontal: 20), + value: selectNamespace, + + /// 选择后的显示 + + items: namespaces + .map((e) => DropdownMenuItem( + value: e, + child: Text("${e.name}(${e.value})", + overflow: TextOverflow.ellipsis), + )) + .toList(), + onChanged: (Namespace? value) { + if (value == null) return; + setState(() { + selectNamespace = value; + }); + }, + ), + ), + ), + ) + ], + ), + ), + Row( + children: [ + SizedBox( + width: 100, + child: Text( + '名称:', + style: TextStyle( + fontWeight: FontWeight.bold, + color: Theme.of(context).textTheme.bodyMedium!.color, + ), + textAlign: TextAlign.right, + ), + ), + Expanded( + child: TextField( + controller: v5NametextEditingController, + decoration: InputDecoration( + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide( + color: Theme.of(context).dividerColor, + ), + ), + filled: true, + hintText: '请输入名称', + hintStyle: const TextStyle(color: Colors.grey), + ), + ), + ), + ], + ), + ], + Padding( + padding: const EdgeInsets.symmetric(vertical: 20), + child: Row( + children: [ + SizedBox( + width: 100, + child: Text( + "生成数量:", + textAlign: TextAlign.right, + style: TextStyle( + fontWeight: FontWeight.bold, + color: Theme.of(context).textTheme.bodyMedium!.color, + ), + ), + ), + Expanded( + child: TextField( + controller: generateCountTextEditingController, + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r"^\d*$")), + ], + decoration: InputDecoration( + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide( + color: Theme.of(context).dividerColor, + ), + ), + filled: true, + hintText: '请输入生成数量', + hintStyle: const TextStyle(color: Colors.grey), + ), + ), + ), + ], + ), + ), + Center( + child: ElevatedButton( + onPressed: () { + final int count = + int.tryParse(generateCountTextEditingController.text) ?? + 1; + if (count <= 0) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('生成数量必须大于0'), + ), + ); + return; + } + if (count > 10000) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('生成数量不能超过10000.'), + ), + ); + return; + } + final List uuids = List.generate( + count, (index) => uuidVersions[selectUuidVersion]!()); + setState(() { + uuidResult = uuids; + }); + }, + child: const Text('生成'), + ), + ) + ], + ), + ), + ); + } + + /// 生成结果 + Widget _buildGenerateResult() { + if (uuidResult.isEmpty) { + return const SizedBox(); + } + return Card( + elevation: 8, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + color: Theme.of(context).brightness == Brightness.dark + ? Colors.grey[850] + : Colors.white, + child: Padding( + padding: const EdgeInsets.all(24.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + '生成结果(${uuidResult.length}个)', + style: TextStyle( + fontWeight: FontWeight.bold, + color: Theme.of(context).textTheme.bodyMedium!.color, + ), + ), + const SizedBox(height: 10), + SingleChildScrollView( + child: Container( + padding: const EdgeInsets.all(10.0), + decoration: BoxDecoration( + color: Theme.of(context).cardColor, + borderRadius: BorderRadius.circular(8.0), + ), + child: SizedBox( + height: max( + 100, + min(300, uuidResult.length * 30.0), + ), + child: SelectableText( + uuidDisplay, + style: const TextStyle(fontSize: 16.0), + ), + ), + ), + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 10), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + // mainAxisSize: MainAxisSize.min, + children: [ + /// 复制到剪辑版 + ElevatedButton( + onPressed: () { + Clipboard.setData(ClipboardData(text: uuidDisplay)); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('已复制到剪贴板'), + ), + ); + }, + child: const Text('复制到剪贴板'), + ), + + /// 下载文件 + ElevatedButton( + onPressed: () { + web.HTMLAnchorElement() + ..href = UriData.fromString(uuidDisplay).toString() + ..setAttribute('download', + 'uuid_list_${DateTime.now().millisecondsSinceEpoch}.txt') + ..click() + ..remove(); + }, + child: const Text('下载文件'), + ), + ], + ), + ) + ], + ), + ), + ); + } + @override Widget build(BuildContext context) { - return const SizedBox(); + return LayoutBuilder( + builder: (context, constraints) { + return ConstrainedBox( + constraints: BoxConstraints(minHeight: constraints.maxHeight), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Center( + child: Container( + padding: const EdgeInsets.all(10.0), + constraints: BoxConstraints( + maxWidth: min(constraints.maxWidth, 800), + ), + child: Column( + children: [ + /// 请选择UUID版本 + _buildGenerateParams(), + _buildGenerateResult() + ], + ), + ), + ), + ], + ), + ); + }, + ); } } diff --git a/lib/world_time/index.dart b/lib/world_time/index.dart index f5bdaeb..c516b91 100644 --- a/lib/world_time/index.dart +++ b/lib/world_time/index.dart @@ -42,7 +42,6 @@ class _WorldTimeState extends State @override void initState() { super.initState(); - web.document.title = "世界时间"; fetchWorldTime(); _initializeVisibilityChangeListener(); } diff --git a/pubspec.lock b/pubspec.lock index 6389b01..1096a1f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -136,6 +136,14 @@ packages: description: flutter source: sdk version: "0.0.0" + go_router: + dependency: "direct main" + description: + name: go_router + sha256: "2fd11229f59e23e967b0775df8d5948a519cd7e1e8b6e849729e010587b46539" + url: "https://pub.flutter-io.cn" + source: hosted + version: "14.6.2" google_fonts: dependency: "direct main" description: @@ -176,6 +184,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "4.0.0" + logging: + dependency: transitive + description: + name: logging + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.3.0" material_color_utilities: dependency: transitive description: @@ -296,14 +312,6 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "2.1.8" - refreshed: - dependency: "direct main" - description: - name: refreshed - sha256: "037291d143c6dffbc8b7539931e0762079da68ed546c5861ea030441c8b4cc4e" - url: "https://pub.flutter-io.cn" - source: hosted - version: "2.10.0" simple_icons: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index bd5d2a5..111e3ac 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -39,10 +39,10 @@ dependencies: cupertino_icons: ^1.0.8 # flutter_quill: ^11.0.0-dev.4 # flutter_quill_extensions: ^11.0.0-dev.4 - refreshed: ^2.9.0 + go_router: ^14.6.2 http: ^1.2.2 fetch_client: ^1.1.2 - web: ^1.0.0 + web: ^1.1.0 uuid: ^4.5.1 google_fonts: ^6.2.1 mobile_scanner: ^6.0.2 diff --git a/web/flutter_bootstrap.js b/web/flutter_bootstrap.js deleted file mode 100644 index 0a2f13f..0000000 --- a/web/flutter_bootstrap.js +++ /dev/null @@ -1,10 +0,0 @@ -{{flutter_js}} -{{flutter_build_config}} - -_flutter.buildConfig.builds[0].mainJsPath += "?v=" + serviceWorkerVersion; - -_flutter.loader.load({ - serviceWorkerSettings: { - serviceWorkerVersion: serviceWorkerVersion - } -}); \ No newline at end of file diff --git a/web/index.html b/web/index.html index 2cdb47b..153dd62 100644 --- a/web/index.html +++ b/web/index.html @@ -34,8 +34,52 @@ +