From aefa695b09b3adaef7dfebb3310805639d2b111f Mon Sep 17 00:00:00 2001 From: tomzds8 <2407164659@qq.com> Date: Wed, 17 Jan 2024 23:42:14 +0800 Subject: [PATCH] Add i18n basic support --- assets/i18n/strings.i18n.json | 22 + assets/i18n/strings_zh.i18n.json | 22 + build.yaml | 7 + flutter_rust_bridge.yaml | 2 +- integration_test/simple_test.dart | 2 +- lib/common/constants.dart | 11 + lib/config_store.dart | 47 ++ lib/i18n/strings.g.dart | 336 ++++++++++ lib/main.dart | 232 +------ lib/pages/frame_page.dart | 48 ++ lib/pages/home_page.dart | 12 + lib/pages/setting_page.dart | 151 +++++ lib/providers/locale_provider.dart | 26 + lib/providers/locale_provider.g.dart | 25 + lib/providers/theme_provider.dart | 23 + lib/providers/theme_provider.g.dart | 25 + lib/{src => }/rust/api/model.dart | 0 lib/{src => }/rust/bridge/bridge.dart | 0 lib/{src => }/rust/discovery/model.dart | 0 lib/{src => }/rust/frb_generated.dart | 0 lib/{src => }/rust/frb_generated.io.dart | 0 lib/{src => }/rust/frb_generated.web.dart | 0 lib/{src => }/rust/logger.dart | 0 lib/src/rust/core/model.dart | 79 --- lib/src/rust/core/model.freezed.dart | 584 ------------------ macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.lock | 241 +++++++- pubspec.yaml | 13 +- 28 files changed, 1034 insertions(+), 876 deletions(-) create mode 100644 assets/i18n/strings.i18n.json create mode 100644 assets/i18n/strings_zh.i18n.json create mode 100644 build.yaml create mode 100644 lib/common/constants.dart create mode 100644 lib/config_store.dart create mode 100644 lib/i18n/strings.g.dart create mode 100644 lib/pages/frame_page.dart create mode 100644 lib/pages/home_page.dart create mode 100644 lib/pages/setting_page.dart create mode 100644 lib/providers/locale_provider.dart create mode 100644 lib/providers/locale_provider.g.dart create mode 100644 lib/providers/theme_provider.dart create mode 100644 lib/providers/theme_provider.g.dart rename lib/{src => }/rust/api/model.dart (100%) rename lib/{src => }/rust/bridge/bridge.dart (100%) rename lib/{src => }/rust/discovery/model.dart (100%) rename lib/{src => }/rust/frb_generated.dart (100%) rename lib/{src => }/rust/frb_generated.io.dart (100%) rename lib/{src => }/rust/frb_generated.web.dart (100%) rename lib/{src => }/rust/logger.dart (100%) delete mode 100644 lib/src/rust/core/model.dart delete mode 100644 lib/src/rust/core/model.freezed.dart diff --git a/assets/i18n/strings.i18n.json b/assets/i18n/strings.i18n.json new file mode 100644 index 0000000..313a66d --- /dev/null +++ b/assets/i18n/strings.i18n.json @@ -0,0 +1,22 @@ +{ + "appTitle": "RustSend", + "home": { + "title": "Home Page" + }, + "setting": { + "title": "Settings", + "brightness": { + "title": "Brightness", + "subTitle": "Current mode: $mode", + "themeMode": { + "system": "Follow system", + "light": "Light mode", + "dark": "Dark mode" + } + }, + "language": { + "title": "Language", + "subTitle": "Current language: $language" + } + } +} \ No newline at end of file diff --git a/assets/i18n/strings_zh.i18n.json b/assets/i18n/strings_zh.i18n.json new file mode 100644 index 0000000..2d76a5b --- /dev/null +++ b/assets/i18n/strings_zh.i18n.json @@ -0,0 +1,22 @@ +{ + "appTitle": "锈船", + "home": { + "title": "主页" + }, + "setting": { + "title": "设置", + "brightness": { + "title": "明暗", + "subTitle": "当前模式: $mode", + "themeMode": { + "system": "跟随系统", + "light": "浅色模式", + "dark": "深色模式" + } + }, + "language": { + "title": "语言", + "subTitle": "当前语言: $language" + } + } +} \ No newline at end of file diff --git a/build.yaml b/build.yaml new file mode 100644 index 0000000..f84c060 --- /dev/null +++ b/build.yaml @@ -0,0 +1,7 @@ +targets: + $default: + builders: + slang_build_runner: + options: + input_directory: assets/i18n + output_directory: lib/i18n # defaulting to lib/gen if input is outside of lib/ \ No newline at end of file diff --git a/flutter_rust_bridge.yaml b/flutter_rust_bridge.yaml index f4ee5c1..5a2e61c 100644 --- a/flutter_rust_bridge.yaml +++ b/flutter_rust_bridge.yaml @@ -1,2 +1,2 @@ rust_input: rust/src/bridge/**/*.rs -dart_output: lib/src/rust \ No newline at end of file +dart_output: lib/rust \ No newline at end of file diff --git a/integration_test/simple_test.dart b/integration_test/simple_test.dart index de9fdb6..6d23109 100644 --- a/integration_test/simple_test.dart +++ b/integration_test/simple_test.dart @@ -1,6 +1,6 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:localsend_rs/main.dart'; -import 'package:localsend_rs/src/rust/frb_generated.dart'; +import 'package:localsend_rs/rust/frb_generated.dart'; import 'package:integration_test/integration_test.dart'; void main() { diff --git a/lib/common/constants.dart b/lib/common/constants.dart new file mode 100644 index 0000000..5de2d73 --- /dev/null +++ b/lib/common/constants.dart @@ -0,0 +1,11 @@ +class Language { + final String name; + final String localeName; + + const Language(this.name, this.localeName); +} + +const Map supportLanguages = { + "zh": Language("中文", "zh"), + "en": Language("English", "en"), +}; diff --git a/lib/config_store.dart b/lib/config_store.dart new file mode 100644 index 0000000..8d0977e --- /dev/null +++ b/lib/config_store.dart @@ -0,0 +1,47 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +class ConfigStore { + static ConfigStore? _instance; + static SharedPreferences? _prefs; + + factory ConfigStore() { + if (_instance == null) { + throw Exception('AppSharedPrefs is not initialized. ' + 'Please call AppSharedPrefs.ensureInitialized before.'); + } + return _instance!; + } + + const ConfigStore._(); + + static ensureInitialized() async { + _prefs ??= await SharedPreferences.getInstance(); + _instance ??= const ConfigStore._(); + } + + static const _themeKey = 'theme'; + static const _localeKey = 'locale'; + + ThemeMode themeMode() { + final themeValue = _prefs!.getInt(_themeKey); + if (themeValue == null) return ThemeMode.system; + + return ThemeMode.values[themeValue]; + } + + Future updateThemeMode(ThemeMode theme) async { + await _prefs!.setInt(_themeKey, theme.index); + } + + String locale() { + final localeValue = _prefs!.getString(_localeKey) ?? Platform.localeName; + return localeValue; + } + + Future updateLocale(String locale) async { + await _prefs!.setString(_localeKey, locale); + } +} diff --git a/lib/i18n/strings.g.dart b/lib/i18n/strings.g.dart new file mode 100644 index 0000000..eddb471 --- /dev/null +++ b/lib/i18n/strings.g.dart @@ -0,0 +1,336 @@ +/// Generated file. Do not edit. +/// +/// Original: assets/i18n +/// To regenerate, run: `dart run slang` +/// +/// Locales: 2 +/// Strings: 20 (10 per locale) +/// +/// Built on 2024-01-17 at 15:30 UTC + +// coverage:ignore-file +// ignore_for_file: type=lint + +import 'package:flutter/widgets.dart'; +import 'package:slang/builder/model/node.dart'; +import 'package:slang_flutter/slang_flutter.dart'; +export 'package:slang_flutter/slang_flutter.dart'; + +const AppLocale _baseLocale = AppLocale.en; + +/// Supported locales, see extension methods below. +/// +/// Usage: +/// - LocaleSettings.setLocale(AppLocale.en) // set locale +/// - Locale locale = AppLocale.en.flutterLocale // get flutter locale from enum +/// - if (LocaleSettings.currentLocale == AppLocale.en) // locale check +enum AppLocale with BaseAppLocale { + en(languageCode: 'en', build: Translations.build), + zh(languageCode: 'zh', build: _StringsZh.build); + + const AppLocale({required this.languageCode, this.scriptCode, this.countryCode, required this.build}); // ignore: unused_element + + @override final String languageCode; + @override final String? scriptCode; + @override final String? countryCode; + @override final TranslationBuilder build; + + /// Gets current instance managed by [LocaleSettings]. + Translations get translations => LocaleSettings.instance.translationMap[this]!; +} + +/// Method A: Simple +/// +/// No rebuild after locale change. +/// Translation happens during initialization of the widget (call of t). +/// Configurable via 'translate_var'. +/// +/// Usage: +/// String a = t.someKey.anotherKey; +/// String b = t['someKey.anotherKey']; // Only for edge cases! +Translations get t => LocaleSettings.instance.currentTranslations; + +/// Method B: Advanced +/// +/// All widgets using this method will trigger a rebuild when locale changes. +/// Use this if you have e.g. a settings page where the user can select the locale during runtime. +/// +/// Step 1: +/// wrap your App with +/// TranslationProvider( +/// child: MyApp() +/// ); +/// +/// Step 2: +/// final t = Translations.of(context); // Get t variable. +/// String a = t.someKey.anotherKey; // Use t variable. +/// String b = t['someKey.anotherKey']; // Only for edge cases! +class TranslationProvider extends BaseTranslationProvider { + TranslationProvider({required super.child}) : super(settings: LocaleSettings.instance); + + static InheritedLocaleData of(BuildContext context) => InheritedLocaleData.of(context); +} + +/// Method B shorthand via [BuildContext] extension method. +/// Configurable via 'translate_var'. +/// +/// Usage (e.g. in a widget's build method): +/// context.t.someKey.anotherKey +extension BuildContextTranslationsExtension on BuildContext { + Translations get t => TranslationProvider.of(this).translations; +} + +/// Manages all translation instances and the current locale +class LocaleSettings extends BaseFlutterLocaleSettings { + LocaleSettings._() : super(utils: AppLocaleUtils.instance); + + static final instance = LocaleSettings._(); + + // static aliases (checkout base methods for documentation) + static AppLocale get currentLocale => instance.currentLocale; + static Stream getLocaleStream() => instance.getLocaleStream(); + static AppLocale setLocale(AppLocale locale, {bool? listenToDeviceLocale = false}) => instance.setLocale(locale, listenToDeviceLocale: listenToDeviceLocale); + static AppLocale setLocaleRaw(String rawLocale, {bool? listenToDeviceLocale = false}) => instance.setLocaleRaw(rawLocale, listenToDeviceLocale: listenToDeviceLocale); + static AppLocale useDeviceLocale() => instance.useDeviceLocale(); + @Deprecated('Use [AppLocaleUtils.supportedLocales]') static List get supportedLocales => instance.supportedLocales; + @Deprecated('Use [AppLocaleUtils.supportedLocalesRaw]') static List get supportedLocalesRaw => instance.supportedLocalesRaw; + static void setPluralResolver({String? language, AppLocale? locale, PluralResolver? cardinalResolver, PluralResolver? ordinalResolver}) => instance.setPluralResolver( + language: language, + locale: locale, + cardinalResolver: cardinalResolver, + ordinalResolver: ordinalResolver, + ); +} + +/// Provides utility functions without any side effects. +class AppLocaleUtils extends BaseAppLocaleUtils { + AppLocaleUtils._() : super(baseLocale: _baseLocale, locales: AppLocale.values); + + static final instance = AppLocaleUtils._(); + + // static aliases (checkout base methods for documentation) + static AppLocale parse(String rawLocale) => instance.parse(rawLocale); + static AppLocale parseLocaleParts({required String languageCode, String? scriptCode, String? countryCode}) => instance.parseLocaleParts(languageCode: languageCode, scriptCode: scriptCode, countryCode: countryCode); + static AppLocale findDeviceLocale() => instance.findDeviceLocale(); + static List get supportedLocales => instance.supportedLocales; + static List get supportedLocalesRaw => instance.supportedLocalesRaw; +} + +// translations + +// Path: +class Translations implements BaseTranslations { + /// Returns the current translations of the given [context]. + /// + /// Usage: + /// final t = Translations.of(context); + static Translations of(BuildContext context) => InheritedLocaleData.of(context).translations; + + /// You can call this constructor and build your own translation instance of this locale. + /// Constructing via the enum [AppLocale.build] is preferred. + Translations.build({Map? overrides, PluralResolver? cardinalResolver, PluralResolver? ordinalResolver}) + : assert(overrides == null, 'Set "translation_overrides: true" in order to enable this feature.'), + $meta = TranslationMetadata( + locale: AppLocale.en, + overrides: overrides ?? {}, + cardinalResolver: cardinalResolver, + ordinalResolver: ordinalResolver, + ) { + $meta.setFlatMapFunction(_flatMapFunction); + } + + /// Metadata for the translations of . + @override final TranslationMetadata $meta; + + /// Access flat map + dynamic operator[](String key) => $meta.getTranslation(key); + + late final Translations _root = this; // ignore: unused_field + + // Translations + String get appTitle => 'RustSend'; + late final _StringsHomeEn home = _StringsHomeEn._(_root); + late final _StringsSettingEn setting = _StringsSettingEn._(_root); +} + +// Path: home +class _StringsHomeEn { + _StringsHomeEn._(this._root); + + final Translations _root; // ignore: unused_field + + // Translations + String get title => 'Home Page'; +} + +// Path: setting +class _StringsSettingEn { + _StringsSettingEn._(this._root); + + final Translations _root; // ignore: unused_field + + // Translations + String get title => 'Settings'; + late final _StringsSettingBrightnessEn brightness = _StringsSettingBrightnessEn._(_root); + late final _StringsSettingLanguageEn language = _StringsSettingLanguageEn._(_root); +} + +// Path: setting.brightness +class _StringsSettingBrightnessEn { + _StringsSettingBrightnessEn._(this._root); + + final Translations _root; // ignore: unused_field + + // Translations + String get title => 'Brightness'; + String subTitle({required Object mode}) => 'Current mode: ${mode}'; + late final _StringsSettingBrightnessThemeModeEn themeMode = _StringsSettingBrightnessThemeModeEn._(_root); +} + +// Path: setting.language +class _StringsSettingLanguageEn { + _StringsSettingLanguageEn._(this._root); + + final Translations _root; // ignore: unused_field + + // Translations + String get title => 'Language'; + String subTitle({required Object language}) => 'Current language: ${language}'; +} + +// Path: setting.brightness.themeMode +class _StringsSettingBrightnessThemeModeEn { + _StringsSettingBrightnessThemeModeEn._(this._root); + + final Translations _root; // ignore: unused_field + + // Translations + String get system => 'Follow system'; + String get light => 'Light mode'; + String get dark => 'Dark mode'; +} + +// Path: +class _StringsZh implements Translations { + /// You can call this constructor and build your own translation instance of this locale. + /// Constructing via the enum [AppLocale.build] is preferred. + _StringsZh.build({Map? overrides, PluralResolver? cardinalResolver, PluralResolver? ordinalResolver}) + : assert(overrides == null, 'Set "translation_overrides: true" in order to enable this feature.'), + $meta = TranslationMetadata( + locale: AppLocale.zh, + overrides: overrides ?? {}, + cardinalResolver: cardinalResolver, + ordinalResolver: ordinalResolver, + ) { + $meta.setFlatMapFunction(_flatMapFunction); + } + + /// Metadata for the translations of . + @override final TranslationMetadata $meta; + + /// Access flat map + @override dynamic operator[](String key) => $meta.getTranslation(key); + + @override late final _StringsZh _root = this; // ignore: unused_field + + // Translations + @override String get appTitle => '锈船'; + @override late final _StringsHomeZh home = _StringsHomeZh._(_root); + @override late final _StringsSettingZh setting = _StringsSettingZh._(_root); +} + +// Path: home +class _StringsHomeZh implements _StringsHomeEn { + _StringsHomeZh._(this._root); + + @override final _StringsZh _root; // ignore: unused_field + + // Translations + @override String get title => '主页'; +} + +// Path: setting +class _StringsSettingZh implements _StringsSettingEn { + _StringsSettingZh._(this._root); + + @override final _StringsZh _root; // ignore: unused_field + + // Translations + @override String get title => '设置'; + @override late final _StringsSettingBrightnessZh brightness = _StringsSettingBrightnessZh._(_root); + @override late final _StringsSettingLanguageZh language = _StringsSettingLanguageZh._(_root); +} + +// Path: setting.brightness +class _StringsSettingBrightnessZh implements _StringsSettingBrightnessEn { + _StringsSettingBrightnessZh._(this._root); + + @override final _StringsZh _root; // ignore: unused_field + + // Translations + @override String get title => '明暗'; + @override String subTitle({required Object mode}) => '当前模式: ${mode}'; + @override late final _StringsSettingBrightnessThemeModeZh themeMode = _StringsSettingBrightnessThemeModeZh._(_root); +} + +// Path: setting.language +class _StringsSettingLanguageZh implements _StringsSettingLanguageEn { + _StringsSettingLanguageZh._(this._root); + + @override final _StringsZh _root; // ignore: unused_field + + // Translations + @override String get title => '语言'; + @override String subTitle({required Object language}) => '当前语言: ${language}'; +} + +// Path: setting.brightness.themeMode +class _StringsSettingBrightnessThemeModeZh implements _StringsSettingBrightnessThemeModeEn { + _StringsSettingBrightnessThemeModeZh._(this._root); + + @override final _StringsZh _root; // ignore: unused_field + + // Translations + @override String get system => '跟随系统'; + @override String get light => '浅色模式'; + @override String get dark => '深色模式'; +} + +/// Flat map(s) containing all translations. +/// Only for edge cases! For simple maps, use the map function of this library. + +extension on Translations { + dynamic _flatMapFunction(String path) { + switch (path) { + case 'appTitle': return 'RustSend'; + case 'home.title': return 'Home Page'; + case 'setting.title': return 'Settings'; + case 'setting.brightness.title': return 'Brightness'; + case 'setting.brightness.subTitle': return ({required Object mode}) => 'Current mode: ${mode}'; + case 'setting.brightness.themeMode.system': return 'Follow system'; + case 'setting.brightness.themeMode.light': return 'Light mode'; + case 'setting.brightness.themeMode.dark': return 'Dark mode'; + case 'setting.language.title': return 'Language'; + case 'setting.language.subTitle': return ({required Object language}) => 'Current language: ${language}'; + default: return null; + } + } +} + +extension on _StringsZh { + dynamic _flatMapFunction(String path) { + switch (path) { + case 'appTitle': return '锈船'; + case 'home.title': return '主页'; + case 'setting.title': return '设置'; + case 'setting.brightness.title': return '明暗'; + case 'setting.brightness.subTitle': return ({required Object mode}) => '当前模式: ${mode}'; + case 'setting.brightness.themeMode.system': return '跟随系统'; + case 'setting.brightness.themeMode.light': return '浅色模式'; + case 'setting.brightness.themeMode.dark': return '深色模式'; + case 'setting.language.title': return '语言'; + case 'setting.language.subTitle': return ({required Object language}) => '当前语言: ${language}'; + default: return null; + } + } +} diff --git a/lib/main.dart b/lib/main.dart index f5d4e40..27e8262 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,14 +3,21 @@ import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:localsend_rs/src/rust/frb_generated.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:localsend_rs/config_store.dart'; +import 'package:localsend_rs/pages/frame_page.dart'; import 'package:path_provider/path_provider.dart'; -import 'src/rust/bridge/bridge.dart'; -import 'src/rust/discovery/model.dart'; +import 'i18n/strings.g.dart'; +import 'providers/locale_provider.dart'; +import 'providers/theme_provider.dart'; +import 'rust/bridge/bridge.dart'; +import 'rust/frb_generated.dart'; Future main() async { WidgetsFlutterBinding.ensureInitialized(); + LocaleSettings.useDeviceLocale(); await RustLib.init(); String storePath; if (Platform.isAndroid) { @@ -29,221 +36,30 @@ Future main() async { // ); await rustSetUp(isDebug: kDebugMode); await setup(); + await ConfigStore.ensureInitialized(); // createLogStream().listen((event) { // print( // 'rust log [${event.level}] - ${event.tag} ${event.msg}(rust_time=${event.timeMillis})'); // }); - runApp(const MyApp()); + runApp(const ProviderScope(child: MyApp())); } -class MyApp extends StatelessWidget { +class MyApp extends ConsumerWidget { const MyApp({super.key}); @override - Widget build(BuildContext context) { + Widget build(BuildContext context, WidgetRef ref) { + final themeMode = ref.watch(themeStateProvider); + final locale = ref.watch(localeStateProvider); return MaterialApp( - home: Scaffold( - appBar: AppBar(title: const Text('flutter_rust_bridge quickstart')), - body: Column( - children: [ - ElevatedButton( - onPressed: () { - start( - // config: const ServerConfig( - // multicastAddr: "224.0.0.167", - // port: 53317, - // protocol: "http", - // download: false, - // announcement: true, - // announce: true, - // ), - ); - }, - child: const Text("start server"), - ), - FutureBuilder( - future: getDownloadsDirectory(), - builder: (context, snapshot) { - if (snapshot.hasData) { - return Text("external storage: ${snapshot.data}"); - } else { - return const Text("external storage: unknown"); - } - }, - ), - ElevatedButton( - onPressed: () async { - await stop(); - }, - child: const Text("stop server"), - ), - ElevatedButton( - onPressed: () async { - await discover(); - }, - child: const Text("discover"), - ), - MissionList(), - Expanded(child: NodeList()), - ], - ), - ), - ); - } -} - -class NodeCard extends StatelessWidget { - const NodeCard({required this.node, super.key}); - - final Node node; - - @override - Widget build(BuildContext context) { - return Container( - width: 200, - height: 100, - child: Card( - child: Row( - children: [ - Icon(Icons.phone_android), - Column( - children: [ - Text(node.alias), - Text(node.address), - ], - ), - ], - ), - ), - ); - } -} - -class MissionWidget extends StatelessWidget { - const MissionWidget({required this.mission, super.key}); - - final MissionItem mission; - - @override - Widget build(BuildContext context) { - return Container( - width: 300, - height: 100, - child: Center( - child: Card( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - children: [ - Expanded( - child: Center( - child: Text("missions"), - ), - ), - Row( - mainAxisSize: MainAxisSize.max, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - FilledButton( - onPressed: () { - acceptMission(missionId: mission.id, accept: true); - }, - child: Text("接收"), - ), - SizedBox(width: 16), - FilledButton( - onPressed: () { - acceptMission(missionId: mission.id, accept: false); - }, - child: Text("拒绝"), - ), - ], - ) - ], - ), - ), - ), - ), - ); - } -} - -class MissionList extends StatefulWidget { - const MissionList({super.key}); - - @override - State createState() => _MissionListState(); -} - -class _MissionListState extends State { - late Stream _missionStream; - - @override - void initState() { - super.initState(); - _missionStream = missionChannel(); - } - - @override - Widget build(BuildContext context) { - return StreamBuilder( - stream: _missionStream, - builder: (context, snapShot) { - if (snapShot.hasData) { - final mission = snapShot.data as MissionItem; - return MissionWidget(mission: mission); - } - return Container(); - }); - } -} - -class NodeList extends StatefulWidget { - const NodeList({super.key}); - - @override - State createState() => _NodeListState(); -} - -class _NodeListState extends State { - late Stream> _nodeStream; - - @override - void initState() { - super.initState(); - _nodeStream = nodeChannel(); - } - - @override - Widget build(BuildContext context) { - return Column( - children: [ - Row( - children: [Text("nodes")], - ), - StreamBuilder( - stream: _nodeStream, - builder: (context, snapshot) { - if (snapshot.hasData) { - final nodes = snapshot.data as List; - return Expanded( - child: ListView.builder( - itemCount: nodes.length, - itemBuilder: (context, index) { - final node = nodes[index]; - return Card( - child: ListTile( - title: Text(node.alias), - subtitle: Text(node.address), - ), - ); - }, - ), - ); - } - return Container(); - }), - ], + title: t.appTitle, + locale: locale, // use provider + supportedLocales: AppLocaleUtils.supportedLocales, + localizationsDelegates: GlobalMaterialLocalizations.delegates, + theme: ThemeData(useMaterial3: true), + darkTheme: ThemeData.dark(useMaterial3: true), + themeMode: themeMode, + home: FramePage(), ); } } diff --git a/lib/pages/frame_page.dart b/lib/pages/frame_page.dart new file mode 100644 index 0000000..27c3303 --- /dev/null +++ b/lib/pages/frame_page.dart @@ -0,0 +1,48 @@ +import 'package:flutter/material.dart'; +import 'package:localsend_rs/i18n/strings.g.dart'; +import 'package:localsend_rs/pages/home_page.dart'; +import 'package:localsend_rs/pages/setting_page.dart'; + +class FramePage extends StatefulWidget { + const FramePage({super.key}); + + @override + State createState() => _FramePageState(); +} + +class _FramePageState extends State { + int index = 0; + + List pages = [ + HomePage(), + const SettingPage(), + ]; + + @override + Widget build(BuildContext context) { + return Scaffold( + body: IndexedStack( + index: index, + children: pages, + ), + bottomNavigationBar: BottomNavigationBar( + currentIndex: index, + onTap: (index) { + setState(() { + this.index = index; + }); + }, + items: [ + BottomNavigationBarItem( + icon: const Icon(Icons.home), + label: t.home.title, + ), + BottomNavigationBarItem( + icon: const Icon(Icons.settings), + label: t.setting.title, + ), + ], + ), + ); + } +} diff --git a/lib/pages/home_page.dart b/lib/pages/home_page.dart new file mode 100644 index 0000000..d4fb213 --- /dev/null +++ b/lib/pages/home_page.dart @@ -0,0 +1,12 @@ +import 'package:flutter/material.dart'; + +class HomePage extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Home Page'), + ), + ); + } +} diff --git a/lib/pages/setting_page.dart b/lib/pages/setting_page.dart new file mode 100644 index 0000000..b70b51a --- /dev/null +++ b/lib/pages/setting_page.dart @@ -0,0 +1,151 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:localsend_rs/i18n/strings.g.dart'; + +import '../common/constants.dart'; +import '../providers/locale_provider.dart'; +import '../providers/theme_provider.dart'; + +class SettingPage extends ConsumerWidget { + const SettingPage({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return Scaffold( + body: CustomScrollView( + slivers: [ + SliverAppBar( + centerTitle: true, + title: Text(t.setting.title), + pinned: true, + ), + SliverList( + delegate: SliverChildListDelegate([ + const ThemeTile(), + // color tile + // language tile + LocaleTile(), + ])) + ], + ), + ); + } +} + +class ThemeTile extends ConsumerWidget { + const ThemeTile({ + super.key, + }); + + String getThemeName(ThemeMode themeMode) { + switch (themeMode) { + case ThemeMode.system: + return t.setting.brightness.themeMode.system; + case ThemeMode.light: + return t.setting.brightness.themeMode.light; + case ThemeMode.dark: + return t.setting.brightness.themeMode.dark; + } + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + final themeMode = ref.watch(themeStateProvider); + return ListTile( + title: Text(t.setting.brightness.title), + subtitle: + Text(t.setting.brightness.subTitle(mode: getThemeName(themeMode))), + trailing: OverflowBar( + children: [ + IconButton( + isSelected: themeMode == ThemeMode.system, + selectedIcon: Icon( + Icons.brightness_auto, + color: Theme.of(context).colorScheme.secondary, + ), + onPressed: () { + ref.read(themeStateProvider.notifier).setTheme(ThemeMode.system); + }, + icon: const Icon(Icons.brightness_auto_outlined), + ), + IconButton( + isSelected: themeMode == ThemeMode.light, + selectedIcon: Icon( + Icons.brightness_5, + color: Theme.of(context).colorScheme.secondary, + ), + onPressed: () { + ref.read(themeStateProvider.notifier).setTheme(ThemeMode.light); + }, + icon: const Icon(Icons.brightness_5_outlined), + ), + IconButton( + isSelected: themeMode == ThemeMode.dark, + selectedIcon: Icon( + Icons.brightness_2, + color: Theme.of(context).colorScheme.secondary, + ), + onPressed: () { + ref.read(themeStateProvider.notifier).setTheme(ThemeMode.dark); + }, + icon: const Icon(Icons.brightness_2_outlined), + ), + ], + ), + ); + } +} + +class LocaleTile extends ConsumerWidget { + const LocaleTile({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + Locale locale = ref.watch(localeStateProvider); + final currentLocaleName = + supportLanguages[locale.languageCode]?.name ?? "unknown"; + + return ListTile( + title: Text(t.setting.language.title), + subtitle: Text(t.setting.language.subTitle(language: currentLocaleName)), + trailing: FilledButton( + onPressed: () { + showDialog( + context: context, + builder: (context) { + final systemLocale = Platform.localeName.split("_")[0]; + final systemLocaleName = + supportLanguages[systemLocale]?.name ?? "unknown"; + return SimpleDialog( + title: Text(t.setting.language.title), + children: [ + ListTile( + title: Text("系统默认: $systemLocaleName"), + onTap: () { + ref.read(localeStateProvider.notifier).setLocale( + Locale(systemLocale), + ); + Navigator.of(context).pop(); + }, + ), + for (var language in supportLanguages.values) + ListTile( + title: Text(language.name), + onTap: () { + ref.read(localeStateProvider.notifier).setLocale( + Locale(language.localeName), + ); + Navigator.of(context).pop(); + }, + ), + ], + ); + }); + }, + child: Text(currentLocaleName), + ), + ); + } +} diff --git a/lib/providers/locale_provider.dart b/lib/providers/locale_provider.dart new file mode 100644 index 0000000..c863942 --- /dev/null +++ b/lib/providers/locale_provider.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +import '../config_store.dart'; +import '../i18n/strings.g.dart'; + +part 'locale_provider.g.dart'; + +@riverpod +class LocaleState extends _$LocaleState { + @override + Locale build() { + final localeValue = ConfigStore().locale(); + return Locale(localeValue); + } + + Locale getLocale() { + return state; + } + + void setLocale(Locale locale) { + state = locale; + ConfigStore().updateLocale(state.languageCode); + LocaleSettings.setLocaleRaw(state.languageCode); + } +} diff --git a/lib/providers/locale_provider.g.dart b/lib/providers/locale_provider.g.dart new file mode 100644 index 0000000..ebb24df --- /dev/null +++ b/lib/providers/locale_provider.g.dart @@ -0,0 +1,25 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'locale_provider.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$localeStateHash() => r'1a73a69558ea869f5190efd35d2e496d8a9ff139'; + +/// See also [LocaleState]. +@ProviderFor(LocaleState) +final localeStateProvider = + AutoDisposeNotifierProvider.internal( + LocaleState.new, + name: r'localeStateProvider', + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') ? null : _$localeStateHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef _$LocaleState = AutoDisposeNotifier; +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member diff --git a/lib/providers/theme_provider.dart b/lib/providers/theme_provider.dart new file mode 100644 index 0000000..34cf027 --- /dev/null +++ b/lib/providers/theme_provider.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +import '../config_store.dart'; + +part 'theme_provider.g.dart'; + +@riverpod +class ThemeState extends _$ThemeState { + @override + ThemeMode build() { + return ConfigStore().themeMode(); + } + + ThemeMode getTheme() { + return state; + } + + void setTheme(ThemeMode mode) { + state = mode; + ConfigStore().updateThemeMode(state); + } +} diff --git a/lib/providers/theme_provider.g.dart b/lib/providers/theme_provider.g.dart new file mode 100644 index 0000000..4117f4e --- /dev/null +++ b/lib/providers/theme_provider.g.dart @@ -0,0 +1,25 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'theme_provider.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$themeStateHash() => r'6f0d061bc0306df79968b8a6a40a50220428ec9d'; + +/// See also [ThemeState]. +@ProviderFor(ThemeState) +final themeStateProvider = + AutoDisposeNotifierProvider.internal( + ThemeState.new, + name: r'themeStateProvider', + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') ? null : _$themeStateHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef _$ThemeState = AutoDisposeNotifier; +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member diff --git a/lib/src/rust/api/model.dart b/lib/rust/api/model.dart similarity index 100% rename from lib/src/rust/api/model.dart rename to lib/rust/api/model.dart diff --git a/lib/src/rust/bridge/bridge.dart b/lib/rust/bridge/bridge.dart similarity index 100% rename from lib/src/rust/bridge/bridge.dart rename to lib/rust/bridge/bridge.dart diff --git a/lib/src/rust/discovery/model.dart b/lib/rust/discovery/model.dart similarity index 100% rename from lib/src/rust/discovery/model.dart rename to lib/rust/discovery/model.dart diff --git a/lib/src/rust/frb_generated.dart b/lib/rust/frb_generated.dart similarity index 100% rename from lib/src/rust/frb_generated.dart rename to lib/rust/frb_generated.dart diff --git a/lib/src/rust/frb_generated.io.dart b/lib/rust/frb_generated.io.dart similarity index 100% rename from lib/src/rust/frb_generated.io.dart rename to lib/rust/frb_generated.io.dart diff --git a/lib/src/rust/frb_generated.web.dart b/lib/rust/frb_generated.web.dart similarity index 100% rename from lib/src/rust/frb_generated.web.dart rename to lib/rust/frb_generated.web.dart diff --git a/lib/src/rust/logger.dart b/lib/rust/logger.dart similarity index 100% rename from lib/src/rust/logger.dart rename to lib/rust/logger.dart diff --git a/lib/src/rust/core/model.dart b/lib/src/rust/core/model.dart deleted file mode 100644 index 9495a05..0000000 --- a/lib/src/rust/core/model.dart +++ /dev/null @@ -1,79 +0,0 @@ -// This file is automatically generated, so please do not edit it. -// Generated by `flutter_rust_bridge`@ 2.0.0-dev.5. - -// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import - -import '../frb_generated.dart'; -import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; -import 'package:freezed_annotation/freezed_annotation.dart' hide protected; -part 'model.freezed.dart'; - -class DeviceInfo { - final String alias; - final String version; - final String deviceModel; - final String deviceType; - final String fingerprint; - final String? address; - final int port; - final String protocol; - final bool download; - final bool announcement; - final bool announce; - - const DeviceInfo({ - required this.alias, - required this.version, - required this.deviceModel, - required this.deviceType, - required this.fingerprint, - this.address, - required this.port, - required this.protocol, - required this.download, - required this.announcement, - required this.announce, - }); - - @override - int get hashCode => - alias.hashCode ^ - version.hashCode ^ - deviceModel.hashCode ^ - deviceType.hashCode ^ - fingerprint.hashCode ^ - address.hashCode ^ - port.hashCode ^ - protocol.hashCode ^ - download.hashCode ^ - announcement.hashCode ^ - announce.hashCode; - - @override - bool operator ==(Object other) => - identical(this, other) || - other is DeviceInfo && - runtimeType == other.runtimeType && - alias == other.alias && - version == other.version && - deviceModel == other.deviceModel && - deviceType == other.deviceType && - fingerprint == other.fingerprint && - address == other.address && - port == other.port && - protocol == other.protocol && - download == other.download && - announcement == other.announcement && - announce == other.announce; -} - -@freezed -sealed class Progress with _$Progress { - const factory Progress.prepare() = Progress_Prepare; - const factory Progress.idle() = Progress_Idle; - const factory Progress.progress( - int field0, - int field1, - ) = Progress_Progress; - const factory Progress.done() = Progress_Done; -} diff --git a/lib/src/rust/core/model.freezed.dart b/lib/src/rust/core/model.freezed.dart deleted file mode 100644 index 1f2e72d..0000000 --- a/lib/src/rust/core/model.freezed.dart +++ /dev/null @@ -1,584 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'model.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); - -/// @nodoc -mixin _$Progress { - @optionalTypeArgs - TResult when({ - required TResult Function() prepare, - required TResult Function() idle, - required TResult Function(int field0, int field1) progress, - required TResult Function() done, - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult? whenOrNull({ - TResult? Function()? prepare, - TResult? Function()? idle, - TResult? Function(int field0, int field1)? progress, - TResult? Function()? done, - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult maybeWhen({ - TResult Function()? prepare, - TResult Function()? idle, - TResult Function(int field0, int field1)? progress, - TResult Function()? done, - required TResult orElse(), - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult map({ - required TResult Function(Progress_Prepare value) prepare, - required TResult Function(Progress_Idle value) idle, - required TResult Function(Progress_Progress value) progress, - required TResult Function(Progress_Done value) done, - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult? mapOrNull({ - TResult? Function(Progress_Prepare value)? prepare, - TResult? Function(Progress_Idle value)? idle, - TResult? Function(Progress_Progress value)? progress, - TResult? Function(Progress_Done value)? done, - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult maybeMap({ - TResult Function(Progress_Prepare value)? prepare, - TResult Function(Progress_Idle value)? idle, - TResult Function(Progress_Progress value)? progress, - TResult Function(Progress_Done value)? done, - required TResult orElse(), - }) => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $ProgressCopyWith<$Res> { - factory $ProgressCopyWith(Progress value, $Res Function(Progress) then) = - _$ProgressCopyWithImpl<$Res, Progress>; -} - -/// @nodoc -class _$ProgressCopyWithImpl<$Res, $Val extends Progress> - implements $ProgressCopyWith<$Res> { - _$ProgressCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; -} - -/// @nodoc -abstract class _$$Progress_PrepareImplCopyWith<$Res> { - factory _$$Progress_PrepareImplCopyWith(_$Progress_PrepareImpl value, - $Res Function(_$Progress_PrepareImpl) then) = - __$$Progress_PrepareImplCopyWithImpl<$Res>; -} - -/// @nodoc -class __$$Progress_PrepareImplCopyWithImpl<$Res> - extends _$ProgressCopyWithImpl<$Res, _$Progress_PrepareImpl> - implements _$$Progress_PrepareImplCopyWith<$Res> { - __$$Progress_PrepareImplCopyWithImpl(_$Progress_PrepareImpl _value, - $Res Function(_$Progress_PrepareImpl) _then) - : super(_value, _then); -} - -/// @nodoc - -class _$Progress_PrepareImpl implements Progress_Prepare { - const _$Progress_PrepareImpl(); - - @override - String toString() { - return 'Progress.prepare()'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && other is _$Progress_PrepareImpl); - } - - @override - int get hashCode => runtimeType.hashCode; - - @override - @optionalTypeArgs - TResult when({ - required TResult Function() prepare, - required TResult Function() idle, - required TResult Function(int field0, int field1) progress, - required TResult Function() done, - }) { - return prepare(); - } - - @override - @optionalTypeArgs - TResult? whenOrNull({ - TResult? Function()? prepare, - TResult? Function()? idle, - TResult? Function(int field0, int field1)? progress, - TResult? Function()? done, - }) { - return prepare?.call(); - } - - @override - @optionalTypeArgs - TResult maybeWhen({ - TResult Function()? prepare, - TResult Function()? idle, - TResult Function(int field0, int field1)? progress, - TResult Function()? done, - required TResult orElse(), - }) { - if (prepare != null) { - return prepare(); - } - return orElse(); - } - - @override - @optionalTypeArgs - TResult map({ - required TResult Function(Progress_Prepare value) prepare, - required TResult Function(Progress_Idle value) idle, - required TResult Function(Progress_Progress value) progress, - required TResult Function(Progress_Done value) done, - }) { - return prepare(this); - } - - @override - @optionalTypeArgs - TResult? mapOrNull({ - TResult? Function(Progress_Prepare value)? prepare, - TResult? Function(Progress_Idle value)? idle, - TResult? Function(Progress_Progress value)? progress, - TResult? Function(Progress_Done value)? done, - }) { - return prepare?.call(this); - } - - @override - @optionalTypeArgs - TResult maybeMap({ - TResult Function(Progress_Prepare value)? prepare, - TResult Function(Progress_Idle value)? idle, - TResult Function(Progress_Progress value)? progress, - TResult Function(Progress_Done value)? done, - required TResult orElse(), - }) { - if (prepare != null) { - return prepare(this); - } - return orElse(); - } -} - -abstract class Progress_Prepare implements Progress { - const factory Progress_Prepare() = _$Progress_PrepareImpl; -} - -/// @nodoc -abstract class _$$Progress_IdleImplCopyWith<$Res> { - factory _$$Progress_IdleImplCopyWith( - _$Progress_IdleImpl value, $Res Function(_$Progress_IdleImpl) then) = - __$$Progress_IdleImplCopyWithImpl<$Res>; -} - -/// @nodoc -class __$$Progress_IdleImplCopyWithImpl<$Res> - extends _$ProgressCopyWithImpl<$Res, _$Progress_IdleImpl> - implements _$$Progress_IdleImplCopyWith<$Res> { - __$$Progress_IdleImplCopyWithImpl( - _$Progress_IdleImpl _value, $Res Function(_$Progress_IdleImpl) _then) - : super(_value, _then); -} - -/// @nodoc - -class _$Progress_IdleImpl implements Progress_Idle { - const _$Progress_IdleImpl(); - - @override - String toString() { - return 'Progress.idle()'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && other is _$Progress_IdleImpl); - } - - @override - int get hashCode => runtimeType.hashCode; - - @override - @optionalTypeArgs - TResult when({ - required TResult Function() prepare, - required TResult Function() idle, - required TResult Function(int field0, int field1) progress, - required TResult Function() done, - }) { - return idle(); - } - - @override - @optionalTypeArgs - TResult? whenOrNull({ - TResult? Function()? prepare, - TResult? Function()? idle, - TResult? Function(int field0, int field1)? progress, - TResult? Function()? done, - }) { - return idle?.call(); - } - - @override - @optionalTypeArgs - TResult maybeWhen({ - TResult Function()? prepare, - TResult Function()? idle, - TResult Function(int field0, int field1)? progress, - TResult Function()? done, - required TResult orElse(), - }) { - if (idle != null) { - return idle(); - } - return orElse(); - } - - @override - @optionalTypeArgs - TResult map({ - required TResult Function(Progress_Prepare value) prepare, - required TResult Function(Progress_Idle value) idle, - required TResult Function(Progress_Progress value) progress, - required TResult Function(Progress_Done value) done, - }) { - return idle(this); - } - - @override - @optionalTypeArgs - TResult? mapOrNull({ - TResult? Function(Progress_Prepare value)? prepare, - TResult? Function(Progress_Idle value)? idle, - TResult? Function(Progress_Progress value)? progress, - TResult? Function(Progress_Done value)? done, - }) { - return idle?.call(this); - } - - @override - @optionalTypeArgs - TResult maybeMap({ - TResult Function(Progress_Prepare value)? prepare, - TResult Function(Progress_Idle value)? idle, - TResult Function(Progress_Progress value)? progress, - TResult Function(Progress_Done value)? done, - required TResult orElse(), - }) { - if (idle != null) { - return idle(this); - } - return orElse(); - } -} - -abstract class Progress_Idle implements Progress { - const factory Progress_Idle() = _$Progress_IdleImpl; -} - -/// @nodoc -abstract class _$$Progress_ProgressImplCopyWith<$Res> { - factory _$$Progress_ProgressImplCopyWith(_$Progress_ProgressImpl value, - $Res Function(_$Progress_ProgressImpl) then) = - __$$Progress_ProgressImplCopyWithImpl<$Res>; - @useResult - $Res call({int field0, int field1}); -} - -/// @nodoc -class __$$Progress_ProgressImplCopyWithImpl<$Res> - extends _$ProgressCopyWithImpl<$Res, _$Progress_ProgressImpl> - implements _$$Progress_ProgressImplCopyWith<$Res> { - __$$Progress_ProgressImplCopyWithImpl(_$Progress_ProgressImpl _value, - $Res Function(_$Progress_ProgressImpl) _then) - : super(_value, _then); - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? field0 = null, - Object? field1 = null, - }) { - return _then(_$Progress_ProgressImpl( - null == field0 - ? _value.field0 - : field0 // ignore: cast_nullable_to_non_nullable - as int, - null == field1 - ? _value.field1 - : field1 // ignore: cast_nullable_to_non_nullable - as int, - )); - } -} - -/// @nodoc - -class _$Progress_ProgressImpl implements Progress_Progress { - const _$Progress_ProgressImpl(this.field0, this.field1); - - @override - final int field0; - @override - final int field1; - - @override - String toString() { - return 'Progress.progress(field0: $field0, field1: $field1)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$Progress_ProgressImpl && - (identical(other.field0, field0) || other.field0 == field0) && - (identical(other.field1, field1) || other.field1 == field1)); - } - - @override - int get hashCode => Object.hash(runtimeType, field0, field1); - - @JsonKey(ignore: true) - @override - @pragma('vm:prefer-inline') - _$$Progress_ProgressImplCopyWith<_$Progress_ProgressImpl> get copyWith => - __$$Progress_ProgressImplCopyWithImpl<_$Progress_ProgressImpl>( - this, _$identity); - - @override - @optionalTypeArgs - TResult when({ - required TResult Function() prepare, - required TResult Function() idle, - required TResult Function(int field0, int field1) progress, - required TResult Function() done, - }) { - return progress(field0, field1); - } - - @override - @optionalTypeArgs - TResult? whenOrNull({ - TResult? Function()? prepare, - TResult? Function()? idle, - TResult? Function(int field0, int field1)? progress, - TResult? Function()? done, - }) { - return progress?.call(field0, field1); - } - - @override - @optionalTypeArgs - TResult maybeWhen({ - TResult Function()? prepare, - TResult Function()? idle, - TResult Function(int field0, int field1)? progress, - TResult Function()? done, - required TResult orElse(), - }) { - if (progress != null) { - return progress(field0, field1); - } - return orElse(); - } - - @override - @optionalTypeArgs - TResult map({ - required TResult Function(Progress_Prepare value) prepare, - required TResult Function(Progress_Idle value) idle, - required TResult Function(Progress_Progress value) progress, - required TResult Function(Progress_Done value) done, - }) { - return progress(this); - } - - @override - @optionalTypeArgs - TResult? mapOrNull({ - TResult? Function(Progress_Prepare value)? prepare, - TResult? Function(Progress_Idle value)? idle, - TResult? Function(Progress_Progress value)? progress, - TResult? Function(Progress_Done value)? done, - }) { - return progress?.call(this); - } - - @override - @optionalTypeArgs - TResult maybeMap({ - TResult Function(Progress_Prepare value)? prepare, - TResult Function(Progress_Idle value)? idle, - TResult Function(Progress_Progress value)? progress, - TResult Function(Progress_Done value)? done, - required TResult orElse(), - }) { - if (progress != null) { - return progress(this); - } - return orElse(); - } -} - -abstract class Progress_Progress implements Progress { - const factory Progress_Progress(final int field0, final int field1) = - _$Progress_ProgressImpl; - - int get field0; - int get field1; - @JsonKey(ignore: true) - _$$Progress_ProgressImplCopyWith<_$Progress_ProgressImpl> get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class _$$Progress_DoneImplCopyWith<$Res> { - factory _$$Progress_DoneImplCopyWith( - _$Progress_DoneImpl value, $Res Function(_$Progress_DoneImpl) then) = - __$$Progress_DoneImplCopyWithImpl<$Res>; -} - -/// @nodoc -class __$$Progress_DoneImplCopyWithImpl<$Res> - extends _$ProgressCopyWithImpl<$Res, _$Progress_DoneImpl> - implements _$$Progress_DoneImplCopyWith<$Res> { - __$$Progress_DoneImplCopyWithImpl( - _$Progress_DoneImpl _value, $Res Function(_$Progress_DoneImpl) _then) - : super(_value, _then); -} - -/// @nodoc - -class _$Progress_DoneImpl implements Progress_Done { - const _$Progress_DoneImpl(); - - @override - String toString() { - return 'Progress.done()'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && other is _$Progress_DoneImpl); - } - - @override - int get hashCode => runtimeType.hashCode; - - @override - @optionalTypeArgs - TResult when({ - required TResult Function() prepare, - required TResult Function() idle, - required TResult Function(int field0, int field1) progress, - required TResult Function() done, - }) { - return done(); - } - - @override - @optionalTypeArgs - TResult? whenOrNull({ - TResult? Function()? prepare, - TResult? Function()? idle, - TResult? Function(int field0, int field1)? progress, - TResult? Function()? done, - }) { - return done?.call(); - } - - @override - @optionalTypeArgs - TResult maybeWhen({ - TResult Function()? prepare, - TResult Function()? idle, - TResult Function(int field0, int field1)? progress, - TResult Function()? done, - required TResult orElse(), - }) { - if (done != null) { - return done(); - } - return orElse(); - } - - @override - @optionalTypeArgs - TResult map({ - required TResult Function(Progress_Prepare value) prepare, - required TResult Function(Progress_Idle value) idle, - required TResult Function(Progress_Progress value) progress, - required TResult Function(Progress_Done value) done, - }) { - return done(this); - } - - @override - @optionalTypeArgs - TResult? mapOrNull({ - TResult? Function(Progress_Prepare value)? prepare, - TResult? Function(Progress_Idle value)? idle, - TResult? Function(Progress_Progress value)? progress, - TResult? Function(Progress_Done value)? done, - }) { - return done?.call(this); - } - - @override - @optionalTypeArgs - TResult maybeMap({ - TResult Function(Progress_Prepare value)? prepare, - TResult Function(Progress_Idle value)? idle, - TResult Function(Progress_Progress value)? progress, - TResult Function(Progress_Done value)? done, - required TResult orElse(), - }) { - if (done != null) { - return done(this); - } - return orElse(); - } -} - -abstract class Progress_Done implements Progress { - const factory Progress_Done() = _$Progress_DoneImpl; -} diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index e777c67..b8e2b22 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -6,7 +6,9 @@ import FlutterMacOS import Foundation import path_provider_foundation +import shared_preferences_foundation func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) + SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) } diff --git a/pubspec.lock b/pubspec.lock index e51ee55..b8f5543 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -17,6 +17,14 @@ packages: url: "https://pub.dev" source: hosted version: "6.2.0" + analyzer_plugin: + dependency: transitive + description: + name: analyzer_plugin + sha256: "9661b30b13a685efaee9f02e5d01ed9f2b423bd889d28a304d02d704aee69161" + url: "https://pub.dev" + source: hosted + version: "0.11.3" args: dependency: transitive description: @@ -85,10 +93,10 @@ packages: dependency: "direct dev" description: name: build_runner - sha256: "67d591d602906ef9201caf93452495ad1812bea2074f04e25dbd7c133785821b" + sha256: "581bacf68f89ec8792f5e5a0b2c4decd1c948e97ce659dc783688c8a88fbec21" url: "https://pub.dev" source: hosted - version: "2.4.7" + version: "2.4.8" build_runner_core: dependency: transitive description: @@ -129,6 +137,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.3" + ci: + dependency: transitive + description: + name: ci + sha256: "145d095ce05cddac4d797a158bc4cf3b6016d1fe63d8c3d2fbd7212590adca13" + url: "https://pub.dev" + source: hosted + version: "0.1.0" cli_util: dependency: transitive description: @@ -177,6 +193,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.3" + csv: + dependency: transitive + description: + name: csv + sha256: "63ed2871dd6471193dffc52c0e6c76fb86269c00244d244297abbb355c84a86e" + url: "https://pub.dev" + source: hosted + version: "5.1.1" cupertino_icons: dependency: "direct main" description: @@ -185,6 +209,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.6" + custom_lint: + dependency: "direct dev" + description: + name: custom_lint + sha256: dfb893ff17c83cf08676c6b64df11d3e53d80590978d7c1fb242afff3ba6dedb + url: "https://pub.dev" + source: hosted + version: "0.5.8" + custom_lint_builder: + dependency: transitive + description: + name: custom_lint_builder + sha256: "8df6634b38a36a6c6cb74a9c0eb02e9ba0b0ab89b29e38e6daa86e8ed2c6288d" + url: "https://pub.dev" + source: hosted + version: "0.5.8" + custom_lint_core: + dependency: transitive + description: + name: custom_lint_core + sha256: "2b235be098d157e244f18ea905a15a18c16a205e30553888fac6544bbf52f03f" + url: "https://pub.dev" + source: hosted + version: "0.5.8" dart_style: dependency: transitive description: @@ -259,6 +307,19 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.3" + flutter_localizations: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_riverpod: + dependency: "direct main" + description: + name: flutter_riverpod + sha256: da9591d1f8d5881628ccd5c25c40e74fc3eef50ba45e40c3905a06e1712412d5 + url: "https://pub.dev" + source: hosted + version: "2.4.9" flutter_rust_bridge: dependency: "direct main" description: @@ -322,6 +383,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.1" + hotreloader: + dependency: transitive + description: + name: hotreloader + sha256: "94ee21a60ea2836500799f3af035dc3212b1562027f1e0031c14e087f0231449" + url: "https://pub.dev" + source: hosted + version: "4.1.0" http_multi_server: dependency: transitive description: @@ -343,6 +412,14 @@ packages: description: flutter source: sdk version: "0.0.0" + intl: + dependency: transitive + description: + name: intl + sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" + url: "https://pub.dev" + source: hosted + version: "0.18.1" io: dependency: transitive description: @@ -359,6 +436,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.7" + json2yaml: + dependency: transitive + description: + name: json2yaml + sha256: da94630fbc56079426fdd167ae58373286f603371075b69bf46d848d63ba3e51 + url: "https://pub.dev" + source: hosted + version: "3.0.1" json_annotation: dependency: transitive description: @@ -583,6 +668,46 @@ packages: url: "https://pub.dev" source: hosted version: "3.2.1" + riverpod: + dependency: transitive + description: + name: riverpod + sha256: "942999ee48b899f8a46a860f1e13cee36f2f77609eb54c5b7a669bb20d550b11" + url: "https://pub.dev" + source: hosted + version: "2.4.9" + riverpod_analyzer_utils: + dependency: transitive + description: + name: riverpod_analyzer_utils + sha256: d4dabc35358413bf4611fcb6abb46308a67c4ef4cd5e69fd3367b11925c59f57 + url: "https://pub.dev" + source: hosted + version: "0.5.0" + riverpod_annotation: + dependency: "direct main" + description: + name: riverpod_annotation + sha256: b70e95fbd5ca7ce42f5148092022971bb2e9843b6ab71e97d479e8ab52e98979 + url: "https://pub.dev" + source: hosted + version: "2.3.3" + riverpod_generator: + dependency: "direct dev" + description: + name: riverpod_generator + sha256: ff8f064f1d7ef3cc6af481bba8e9a3fcdb4d34df34fac1b39bbc003167065be0 + url: "https://pub.dev" + source: hosted + version: "2.3.9" + riverpod_lint: + dependency: "direct dev" + description: + name: riverpod_lint + sha256: "944929ef82c9bfeaa455ccab97920abcf847a0ffed5c9f6babc520a95db25176" + url: "https://pub.dev" + source: hosted + version: "2.3.7" rust_builder: dependency: "direct main" description: @@ -590,6 +715,70 @@ packages: relative: true source: path version: "0.0.1" + rxdart: + dependency: transitive + description: + name: rxdart + sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb" + url: "https://pub.dev" + source: hosted + version: "0.27.7" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + sha256: "81429e4481e1ccfb51ede496e916348668fd0921627779233bd24cc3ff6abd02" + url: "https://pub.dev" + source: hosted + version: "2.2.2" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "8568a389334b6e83415b6aae55378e158fbc2314e074983362d20c562780fb06" + url: "https://pub.dev" + source: hosted + version: "2.2.1" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "7708d83064f38060c7b39db12aefe449cb8cdc031d6062280087bc4cdb988f5c" + url: "https://pub.dev" + source: hosted + version: "2.3.5" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: "7b15ffb9387ea3e237bb7a66b8a23d2147663d391cafc5c8f37b2e7b4bde5d21" + url: "https://pub.dev" + source: hosted + version: "2.2.2" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59" + url: "https://pub.dev" + source: hosted + version: "2.3.2" shelf: dependency: transitive description: @@ -611,6 +800,30 @@ packages: description: flutter source: sdk version: "0.0.99" + slang: + dependency: "direct main" + description: + name: slang + sha256: "77fd99f7b0da15e671ef0289b24a0a63e74f693c58a0ca54111388e4c0ddb1dd" + url: "https://pub.dev" + source: hosted + version: "3.28.0" + slang_build_runner: + dependency: "direct dev" + description: + name: slang_build_runner + sha256: "387a3d569da4490b1fffbf31f203021fbfd34f15228d83e14a0f40bc940966fa" + url: "https://pub.dev" + source: hosted + version: "3.28.0" + slang_flutter: + dependency: "direct main" + description: + name: slang_flutter + sha256: "57817bb15553bb5df37aed3bac497286bdd8c2eab6763f4de6815efe2c0becee" + url: "https://pub.dev" + source: hosted + version: "3.28.0" source_gen: dependency: transitive description: @@ -627,6 +840,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.0" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.dev" + source: hosted + version: "7.0.0" stack_trace: dependency: transitive description: @@ -635,6 +856,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.11.1" + state_notifier: + dependency: transitive + description: + name: state_notifier + sha256: b8677376aa54f2d7c58280d5a007f9e8774f1968d1fb1c096adcb4792fba29bb + url: "https://pub.dev" + source: hosted + version: "1.0.0" stream_channel: dependency: transitive description: @@ -699,6 +928,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.2" + uuid: + dependency: transitive + description: + name: uuid + sha256: cd210a09f7c18cbe5a02511718e0334de6559871052c90a90c0cca46a4aa81c8 + url: "https://pub.dev" + source: hosted + version: "4.3.3" vector_math: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index c8b3edd..081479d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -30,6 +30,8 @@ environment: dependencies: flutter: sdk: flutter + flutter_localizations: # add this + sdk: flutter # The following adds the Cupertino Icons font to your application. @@ -42,6 +44,11 @@ dependencies: freezed_annotation: ^2.4.1 path_provider: ^2.1.1 permission_handler: ^11.1.0 + flutter_riverpod: ^2.4.9 + riverpod_annotation: ^2.3.3 + shared_preferences: ^2.2.2 + slang: ^3.28.0 + slang_flutter: ^3.28.0 dev_dependencies: flutter_test: @@ -56,8 +63,12 @@ dev_dependencies: ffigen: 8.0.2 integration_test: sdk: flutter - build_runner: ^2.4.7 + build_runner: ^2.4.8 freezed: ^2.4.6 + riverpod_generator: ^2.3.9 + custom_lint: ^0.5.8 + riverpod_lint: ^2.3.7 + slang_build_runner: ^3.28.0 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec