├── .fvm └── fvm_config.json ├── .fvmrc ├── .gitattributes ├── .github └── workflows │ └── cd.yml ├── .gitignore ├── .metadata ├── .vscode ├── launch.json └── settings.json ├── LICENSE ├── Makefile ├── README.md ├── analysis_options.yaml ├── assets └── images │ └── app-icon.png ├── bin └── rewrites_version_json.sh ├── lib ├── app.dart ├── core │ ├── data │ │ ├── package_info │ │ │ ├── package_info.dart │ │ │ └── package_info.g.dart │ │ └── shared_preferences │ │ │ ├── shared_preferences.dart │ │ │ └── shared_preferences.g.dart │ ├── exception │ │ └── app_exception.dart │ ├── ui │ │ └── component │ │ │ ├── bottom_sheet.dart │ │ │ ├── layout.dart │ │ │ ├── material.dart │ │ │ ├── messenger.dart │ │ │ ├── messenger.g.dart │ │ │ ├── responsive.dart │ │ │ └── widget_ref_x.dart │ └── use_case │ │ └── use_case.dart ├── feature │ ├── app │ │ ├── state │ │ │ ├── app_version.dart │ │ │ └── app_version.g.dart │ │ └── ui │ │ │ └── component │ │ │ ├── app_version.dart │ │ │ ├── theme.dart │ │ │ └── theme.g.dart │ ├── color │ │ ├── state │ │ │ ├── color_scheme.dart │ │ │ ├── color_scheme.freezed.dart │ │ │ ├── color_scheme.g.dart │ │ │ ├── current_color_scheme.dart │ │ │ ├── current_color_scheme.g.dart │ │ │ ├── current_dynamic_scheme_variant.dart │ │ │ ├── current_dynamic_scheme_variant.g.dart │ │ │ ├── current_hover_color.dart │ │ │ ├── current_hover_color.g.dart │ │ │ ├── current_seed_color.dart │ │ │ ├── current_seed_color.g.dart │ │ │ ├── palette_item.dart │ │ │ ├── palette_item.freezed.dart │ │ │ ├── tonal_palette.dart │ │ │ ├── tonal_palette.freezed.dart │ │ │ └── tonal_palette.g.dart │ │ └── ui │ │ │ └── component │ │ │ ├── color_scheme.dart │ │ │ ├── palette.dart │ │ │ └── tonal_palette.dart │ ├── home │ │ └── ui │ │ │ └── page │ │ │ ├── component │ │ │ ├── copy_right.dart │ │ │ ├── home_drawer.dart │ │ │ ├── home_panel.dart │ │ │ ├── home_title.dart │ │ │ ├── launch_github_button.dart │ │ │ └── seed_color.dart │ │ │ └── home_page.dart │ ├── launcher │ │ ├── component │ │ │ └── widget_ref_x.dart │ │ └── use_case │ │ │ ├── launch_url.dart │ │ │ ├── launch_url.freezed.dart │ │ │ └── launch_url.g.dart │ ├── seed_color_history │ │ ├── entity │ │ │ ├── seed_color_history.dart │ │ │ ├── seed_color_history.g.dart │ │ │ ├── seed_color_history_collection.dart │ │ │ └── seed_color_history_collection.g.dart │ │ ├── state │ │ │ ├── seed_color_history_collection.dart │ │ │ └── seed_color_history_collection.g.dart │ │ ├── ui │ │ │ └── component │ │ │ │ ├── dynamic_scheme_variant.dart │ │ │ │ ├── popup_menu.dart │ │ │ │ └── seed_color_history.dart │ │ └── use_case │ │ │ ├── add_seed_color_history.dart │ │ │ ├── add_seed_color_history.g.dart │ │ │ ├── delete_seed_color_history.dart │ │ │ └── delete_seed_color_history.g.dart │ └── theme_mode │ │ ├── state │ │ ├── current_brightness.dart │ │ ├── current_brightness.g.dart │ │ ├── current_theme_mode.dart │ │ └── current_theme_mode.g.dart │ │ └── ui │ │ └── component │ │ └── toggle_theme_mode_button.dart ├── main.dart └── util │ ├── assets │ └── assets.gen.dart │ ├── extensions.dart │ ├── logger.dart │ └── provider_logger.dart ├── pubspec.lock ├── pubspec.yaml └── web ├── favicon.ico ├── favicon.png ├── icons ├── Icon-192.png ├── Icon-512.png ├── Icon-maskable-192.png └── Icon-maskable-512.png ├── index.html └── manifest.json /.fvm/fvm_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "flutterSdkVersion": "3.32.1" 3 | } 4 | -------------------------------------------------------------------------------- /.fvmrc: -------------------------------------------------------------------------------- 1 | { 2 | "flutter": "3.32.1", 3 | "flavors": {} 4 | } 5 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # see: https://zenn.dev/mamushi/articles/hide_generated_file_diff 2 | *.freezed.dart linguist-generated=true 3 | *.g.dart linguist-generated=true 4 | *.gen.dart linguist-generated=true 5 | *.gr.dart linguist-generated=true 6 | -------------------------------------------------------------------------------- /.github/workflows/cd.yml: -------------------------------------------------------------------------------- 1 | name: Deploy to GitHub Pages 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | workflow_dispatch: 9 | 10 | jobs: 11 | deploy: 12 | runs-on: ubuntu-latest 13 | timeout-minutes: 30 14 | steps: 15 | - name: Check out repository 16 | uses: actions/checkout@v3 17 | 18 | - name: Check fvm 19 | uses: kuhnroyal/flutter-fvm-config-action@v1 20 | 21 | - name: Setup flutter environment 22 | uses: subosito/flutter-action@v2 23 | with: 24 | flutter-version: ${{ env.FLUTTER_VERSION }} 25 | channel: ${{ env.FLUTTER_CHANNEL }} 26 | cache: true 27 | cache-key: 'flutter-:os:-:channel:-:version:-:arch:-:hash:' 28 | cache-path: '${{ runner.tool_cache }}/flutter/:channel:-:version:-:arch:' 29 | 30 | - name: Cache Pub 31 | uses: actions/cache@v3 32 | id: pub-cache 33 | with: 34 | path: | 35 | ${{ env.FLUTTER_HOME }}/.pub-cache 36 | **/.packages 37 | **/.flutter-plugins 38 | **/.flutter-plugin-dependencies 39 | **/.dart_tool/package_config.json 40 | key: build-pubspec-${{ hashFiles('**/pubspec.lock') }} 41 | restore-keys: build-pubspec- 42 | 43 | - name: Read Repository 44 | id: version 45 | run: | 46 | REPOSITORY=$(echo ${{ github.repository }} | sed -e "s#.*/##") 47 | echo ::set-output name=repository::$REPOSITORY 48 | 49 | - name: Build web 50 | run: | 51 | flutter config --enable-web 52 | flutter build web \ 53 | --release \ 54 | --base-href /${{ steps.version.outputs.repository }}/ 55 | bin/rewrites_version_json.sh ${{ github.run_number }} 56 | 57 | - name: Deploy to GitHub Pages 58 | uses: peaceiris/actions-gh-pages@v3 59 | with: 60 | github_token: ${{ secrets.GITHUB_TOKEN }} 61 | publish_dir: ./build/web 62 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | **/doc/api/ 26 | **/ios/Flutter/.last_build_id 27 | .dart_tool/ 28 | .flutter-plugins 29 | .flutter-plugins-dependencies 30 | .packages 31 | .pub-cache/ 32 | .pub/ 33 | /build/ 34 | 35 | # Symbolication related 36 | app.*.symbols 37 | 38 | # Obfuscation related 39 | app.*.map.json 40 | 41 | # Android Studio will place build artifacts here 42 | /android/app/debug 43 | /android/app/profile 44 | /android/app/release 45 | 46 | 47 | # FVM Version Cache 48 | .fvm/ -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: "761747bfc538b5af34aa0d3fac380f1bc331ec49" 8 | channel: "stable" 9 | 10 | project_type: app 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 17 | base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 18 | - platform: web 19 | create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 20 | base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 21 | 22 | # User provided section 23 | 24 | # List of Local paths (relative to this file) that should be 25 | # ignored by the migrate tool. 26 | # 27 | # Files that are not part of the templates will be ignored by default. 28 | unmanaged_files: 29 | - 'lib/main.dart' 30 | - 'ios/Runner.xcodeproj/project.pbxproj' 31 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "web", 9 | "type": "dart", 10 | "request": "launch", 11 | "flutterMode": "debug", 12 | "program": "lib/main.dart", 13 | "args": [ 14 | "--web-hostname", 15 | "localhost", 16 | "--web-port", 17 | "5000", 18 | ] 19 | }, 20 | { 21 | "name": "mobile", 22 | "type": "dart", 23 | "request": "launch", 24 | "flutterMode": "debug", 25 | "program": "lib/main.dart", 26 | "args": [] 27 | } 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "debug.openDebug": "openOnDebugBreak", 3 | "editor.rulers": [ 4 | 80 5 | ], 6 | "editor.renderWhitespace": "all", 7 | "editor.renderControlCharacters": true, 8 | "editor.minimap.enabled": false, 9 | "editor.bracketPairColorization.enabled": true, 10 | "[dart]": { 11 | "editor.formatOnSave": true, 12 | "editor.formatOnType": true, 13 | "editor.selectionHighlight": false, 14 | "editor.suggest.snippetsPreventQuickSuggestions": false, 15 | "editor.suggestSelection": "recentlyUsedByPrefix", 16 | "editor.tabCompletion": "onlySnippets", 17 | "editor.wordBasedSuggestions": "off", 18 | "editor.codeActionsOnSave": { 19 | "source.organizeImports": "always", 20 | "source.addMissingImports": "always", 21 | "quickfix.insertSemicolon": "always", 22 | "source.fixAll": "always" 23 | } 24 | }, 25 | "[css]": { 26 | "editor.formatOnSave": true, 27 | "editor.formatOnType": true 28 | }, 29 | "dart.flutterSdkPath": ".fvm/versions/3.24.4", 30 | "dart.debugSdkLibraries": false, 31 | "dart.showSkippedTests": false, 32 | "dart.runPubGetOnPubspecChanges": "always", 33 | "search.exclude": { 34 | "**/.fvm": true, 35 | "**/*.freezed.dart": true, 36 | "**/*.g.dart": true 37 | }, 38 | "files.watcherExclude": { 39 | "**/.fvm": true 40 | }, 41 | "git.autofetch": true, 42 | "explorer.confirmDragAndDrop": false, 43 | "explorer.fileNesting.enabled": true, 44 | "explorer.fileNesting.expand": false, 45 | "explorer.fileNesting.patterns": { 46 | "pubspec.yaml": ".flutter-plugins, .packages, .dart_tool, .flutter-plugins-dependencies, .metadata, template.iml, .packages, pubspec.lock, build.yaml, analysis_options.yaml, all_lint_rules.yaml, flutter_launcher_icons-*.yaml, flutter_native_splash.yaml, lefthook.yaml", 47 | ".env.example": ".env.*", 48 | ".gitignore": ".gitattributes, .gitmodules, .gitmessage, .mailmap, .git-blame*", 49 | "readme.*": "authors, backers.md, changelog*, citation*, code_of_conduct.md, codeowners, contributing.md, contributors, copying, credits, governance.md, history.md, license*, maintainers, readme*, security.md, sponsors.md", 50 | "*.dart": "$(capture).g.dart, $(capture).gr.dart, $(capture).freezed.dart" 51 | }, 52 | "cSpell.enabled": false 53 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 susatthi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | FVM := $(shell which fvm) 2 | FLUTTER := $(FVM) flutter 3 | 4 | .PHONY: pub-get 5 | pub-get: 6 | $(FLUTTER) pub get 7 | 8 | .PHONY: pub-upgrade 9 | pub-upgrade: 10 | $(FLUTTER) pub upgrade 11 | 12 | .PHONY: clean 13 | clean: 14 | $(FLUTTER) clean 15 | rm -rf ios/Pods ios/Podfile.lock 16 | 17 | .PHONY: build-runner-build 18 | build-runner-build: 19 | $(FLUTTER) packages pub run build_runner build --delete-conflicting-outputs 20 | 21 | .PHONY: build-runner-watch 22 | build-runner-watch: 23 | $(FLUTTER) packages pub run build_runner clean 24 | $(FLUTTER) packages pub run build_runner watch --delete-conflicting-outputs 25 | 26 | .PHONY: flutter-launcher-icons 27 | flutter-launcher-icons: 28 | $(FLUTTER) pub run flutter_launcher_icons:main 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Material Color System 2 | 3 | Material Design 3 のカラーシステムを確認できるサイトです。配色に悩んだときにご利用ください。 4 | 5 | https://susatthi.github.io/flutter-material-color-system/ 6 | 7 | https://github.com/susatthi/flutter-material-color-system/assets/13707135/5eb575d6-f092-4a09-b81e-57bcaf7d8d6b 8 | 9 | 10 | ライトモード|ダークモード 11 | --|-- 12 | ![](https://github.com/susatthi/flutter-material-color-system/assets/13707135/7c8c6462-ae93-48a3-9c2f-811767e4b3e3)|![](https://github.com/susatthi/flutter-material-color-system/assets/13707135/0dd326ff-8991-476c-a2bf-1290331ae3af) 13 | 14 | ## 使い方 15 | 16 | デスクトップでお使いください。スマホでも使えますがマウスホバーが効かないので使いづらいと思います。 17 | 18 | - カラーピッカーからシード色とダイナミックスキーム値を変更できます。 19 | - シード色を最大10個まで保存できます。 20 | - カラーをクリックするとHEX値をコピーします。 21 | - ライトテーマとダークテーマの切替ができます。 22 | - レスポンシブ対応です。 23 | 24 | ## 参考サイト 25 | 26 | - 本家サイト 27 | - https://m3.material.io/theme-builder#/custom 28 | - Tonal palettes について 29 | - https://m3.material.io/styles/color/the-color-system/key-colors-tones#a828e350-1551-45e5-8430-eb643e6a7713 30 | 31 | ## カラースキームの設定方法 32 | 33 | カラースキームは次のように `seedColor` と `schemeVariant` から作成しています。詳しくはコードを見てください。 34 | 35 | ```dart 36 | @riverpod 37 | class CurrentSeedColorNotifier extends _$CurrentSeedColorNotifier { 38 | @override 39 | Color build() => const Color(0xFF6750A4); 40 | } 41 | 42 | @riverpod 43 | ThemeData theme(ThemeRef ref, Brightness brightness) { 44 | final seedColor = ref.watch(currentSeedColorNotifierProvider); 45 | final schemeVariant = ref.watch(currentDynamicSchemeVariantNotifierProvider); 46 | return ThemeData( 47 | useMaterial3: true, 48 | colorScheme: ColorScheme.fromSeed( 49 | seedColor: seedColor, 50 | brightness: brightness, 51 | dynamicSchemeVariant: schemeVariant, 52 | ), 53 | ); 54 | } 55 | class App extends ConsumerWidget { 56 | const App({super.key}); 57 | 58 | @override 59 | Widget build(BuildContext context, WidgetRef ref) { 60 | return MaterialApp( 61 | theme: ref.watch(themeProvider(Brightness.light)), 62 | darkTheme: ref.watch(themeProvider(Brightness.dark)), 63 | ・・・ 64 | ); 65 | } 66 | } 67 | 68 | ``` 69 | 70 | ## 環境 71 | 72 | ``` 73 | Flutter 3.32.1 • channel stable • https://github.com/flutter/flutter.git 74 | Framework • revision b25305a883 (16 hours ago) • 2025-05-29 10:40:06 -0700 75 | Engine • revision 1425e5e9ec (2 days ago) • 2025-05-28 14:26:27 -0700 76 | Tools • Dart 3.8.1 • DevTools 2.45.1 77 | ``` 78 | ## LICENSE 79 | 80 | MIT 81 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # https://pub.dev/packages/pedantic_mono 2 | include: package:pedantic_mono/analysis_options.yaml 3 | 4 | linter: 5 | rules: 6 | avoid_classes_with_only_static_members: false 7 | cascade_invocations: false 8 | comment_references: false 9 | constant_identifier_names: true 10 | library_private_types_in_public_api: false 11 | one_member_abstracts: false 12 | prefer_relative_imports: true 13 | use_build_context_synchronously: false 14 | lines_longer_than_80_chars: false 15 | use_setters_to_change_properties: false 16 | 17 | # see: https://github.com/rrousselGit/freezed/issues/488#issuecomment-894358980 18 | analyzer: 19 | exclude: 20 | - '**/*.freezed.dart' 21 | - '**/*.g.dart' 22 | - '**/*.gr.dart' 23 | - '**/*.gen.dart' 24 | - "**/generated_plugin_registrant.dart" 25 | - "**/firebase_options_*.dart" 26 | errors: 27 | invalid_annotation_target: ignore 28 | todo: ignore 29 | plugins: 30 | - custom_lint 31 | 32 | custom_lint: 33 | rules: 34 | - missing_provider_scope: false 35 | -------------------------------------------------------------------------------- /assets/images/app-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/susatthi/flutter-material-color-system/27b4b3e9a65b2979341b0f7a7312233ce31c0644/assets/images/app-icon.png -------------------------------------------------------------------------------- /bin/rewrites_version_json.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | BUILD_NUMBER=$1 4 | VERSION_JSON_PATH=build/web/version.json 5 | 6 | # version.json を書き換える 7 | sed -i -e "s/\"}/\",\"build_number\":\"$BUILD_NUMBER\"}/g" ${VERSION_JSON_PATH} 8 | 9 | rm -rf ${VERSION_JSON_PATH}-e 10 | -------------------------------------------------------------------------------- /lib/app.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 3 | import 'package:responsive_framework/responsive_wrapper.dart'; 4 | 5 | import 'core/ui/component/material.dart'; 6 | import 'core/ui/component/messenger.dart'; 7 | import 'feature/app/ui/component/theme.dart'; 8 | import 'feature/home/ui/page/home_page.dart'; 9 | import 'feature/launcher/component/widget_ref_x.dart'; 10 | import 'feature/theme_mode/state/current_theme_mode.dart'; 11 | 12 | class App extends ConsumerWidget { 13 | const App({super.key}); 14 | 15 | @override 16 | Widget build(BuildContext context, WidgetRef ref) { 17 | return MaterialApp( 18 | title: 'Material Color System', 19 | theme: ref.watch(themeProvider(Brightness.light)), 20 | darkTheme: ref.watch(themeProvider(Brightness.dark)), 21 | themeMode: ref.watch(currentThemeModeProvider), 22 | debugShowCheckedModeBanner: false, 23 | scaffoldMessengerKey: ref.watch(scaffoldMessengerKeyProvider), 24 | home: const _App( 25 | child: HomePage(), 26 | ), 27 | ); 28 | } 29 | } 30 | 31 | class _App extends ConsumerStatefulWidget { 32 | const _App({ 33 | required this.child, 34 | }); 35 | 36 | final Widget? child; 37 | 38 | @override 39 | ConsumerState<_App> createState() => _AppState(); 40 | } 41 | 42 | class _AppState extends ConsumerState<_App> { 43 | @override 44 | Widget build(BuildContext context) { 45 | // Launcherを監視する 46 | ref.listenLauncher(); 47 | 48 | return ResponsiveWrapper.builder( 49 | Navigator( 50 | key: ref.watch(navigatorKeyProvider), 51 | onDidRemovePage: (page) {}, 52 | pages: [ 53 | MaterialPage( 54 | child: widget.child!, 55 | ), 56 | ], 57 | ), 58 | breakpoints: [ 59 | const ResponsiveBreakpoint.resize(420, name: MOBILE), 60 | const ResponsiveBreakpoint.resize(600, name: TABLET), 61 | const ResponsiveBreakpoint.resize(1000, name: DESKTOP), 62 | ], 63 | minWidth: 420, 64 | defaultScale: true, 65 | background: Consumer( 66 | builder: (context, ref, _) { 67 | return Container( 68 | color: context.surface, 69 | ); 70 | }, 71 | ), 72 | ); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /lib/core/data/package_info/package_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 2 | import 'package:package_info_plus/package_info_plus.dart'; 3 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 4 | 5 | part 'package_info.g.dart'; 6 | 7 | @riverpod 8 | PackageInfo packageInfo(Ref ref) { 9 | throw UnimplementedError(); 10 | } 11 | -------------------------------------------------------------------------------- /lib/core/data/package_info/package_info.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'package_info.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$packageInfoHash() => r'a986ce57bb01b5d3b0b5daa0ce54b4b9052e9183'; 10 | 11 | /// See also [packageInfo]. 12 | @ProviderFor(packageInfo) 13 | final packageInfoProvider = AutoDisposeProvider.internal( 14 | packageInfo, 15 | name: r'packageInfoProvider', 16 | debugGetCreateSourceHash: 17 | const bool.fromEnvironment('dart.vm.product') ? null : _$packageInfoHash, 18 | dependencies: null, 19 | allTransitiveDependencies: null, 20 | ); 21 | 22 | @Deprecated('Will be removed in 3.0. Use Ref instead') 23 | // ignore: unused_element 24 | typedef PackageInfoRef = AutoDisposeProviderRef; 25 | // ignore_for_file: type=lint 26 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package 27 | -------------------------------------------------------------------------------- /lib/core/data/shared_preferences/shared_preferences.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | import 'package:shared_preferences/shared_preferences.dart'; 4 | 5 | part 'shared_preferences.g.dart'; 6 | 7 | @riverpod 8 | SharedPreferences sharedPreferences(Ref ref) { 9 | throw UnimplementedError(); 10 | } 11 | -------------------------------------------------------------------------------- /lib/core/data/shared_preferences/shared_preferences.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'shared_preferences.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$sharedPreferencesHash() => r'9ce5d3a1d8e34e1852c77b7a602fe3158dd8f0ca'; 10 | 11 | /// See also [sharedPreferences]. 12 | @ProviderFor(sharedPreferences) 13 | final sharedPreferencesProvider = 14 | AutoDisposeProvider.internal( 15 | sharedPreferences, 16 | name: r'sharedPreferencesProvider', 17 | debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') 18 | ? null 19 | : _$sharedPreferencesHash, 20 | dependencies: null, 21 | allTransitiveDependencies: null, 22 | ); 23 | 24 | @Deprecated('Will be removed in 3.0. Use Ref instead') 25 | // ignore: unused_element 26 | typedef SharedPreferencesRef = AutoDisposeProviderRef; 27 | // ignore_for_file: type=lint 28 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package 29 | -------------------------------------------------------------------------------- /lib/core/exception/app_exception.dart: -------------------------------------------------------------------------------- 1 | abstract class AppException implements Exception { 2 | const AppException(this.message); 3 | 4 | final Object message; 5 | } 6 | -------------------------------------------------------------------------------- /lib/core/ui/component/bottom_sheet.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:gap/gap.dart'; 3 | 4 | import 'material.dart'; 5 | 6 | /// ボトムシート用のScaffold 7 | class BottomSheetContainer extends StatelessWidget { 8 | const BottomSheetContainer({ 9 | super.key, 10 | required this.child, 11 | this.scrollController, 12 | this.bottomPadding = verticalPadding, 13 | this.height, 14 | }); 15 | 16 | final Widget child; 17 | final ScrollController? scrollController; 18 | final double bottomPadding; 19 | final double? height; 20 | 21 | static const verticalPadding = 8.0; 22 | 23 | @override 24 | Widget build(BuildContext context) { 25 | return SafeArea( 26 | child: Padding( 27 | padding: EdgeInsets.only( 28 | bottom: MediaQuery.of(context).viewInsets.bottom, 29 | ), 30 | child: SingleChildScrollView( 31 | controller: scrollController, 32 | child: SizedBox( 33 | height: height, 34 | child: Column( 35 | children: [ 36 | const Gap(verticalPadding), 37 | const _GrabMark(), 38 | const Gap(verticalPadding), 39 | child, 40 | Gap(bottomPadding), 41 | ], 42 | ), 43 | ), 44 | ), 45 | ), 46 | ); 47 | } 48 | } 49 | 50 | class _GrabMark extends StatelessWidget { 51 | const _GrabMark(); 52 | 53 | @override 54 | Widget build(BuildContext context) { 55 | return SizedBox( 56 | width: 32, 57 | height: 4, 58 | child: DecoratedBox( 59 | decoration: BoxDecoration( 60 | color: context.surfaceContainerHighest, 61 | borderRadius: BorderRadius.circular(10), 62 | ), 63 | ), 64 | ); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /lib/core/ui/component/layout.dart: -------------------------------------------------------------------------------- 1 | const paletteHeight = 56.0; 2 | const pickerPanelWidth = 320.0; 3 | const snackBarWidth = 320.0; 4 | 5 | const p4 = 4.0; 6 | const p8 = 8.0; 7 | const p16 = 16.0; 8 | const p24 = 24.0; 9 | const p32 = 32.0; 10 | -------------------------------------------------------------------------------- /lib/core/ui/component/material.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:material_color_utilities/material_color_utilities.dart'; 3 | 4 | import '../../../util/extensions.dart'; 5 | 6 | extension BuildContextX on BuildContext { 7 | /// ダークモードかどうかを返す 8 | bool get isDark => Theme.of(this).brightness == Brightness.dark; 9 | 10 | /// カラーを返す 11 | ColorScheme get _colorScheme => Theme.of(this).colorScheme; 12 | 13 | Color get primary => _colorScheme.primary; 14 | Color get onPrimary => _colorScheme.onPrimary; 15 | Color get primaryContainer => _colorScheme.primaryContainer; 16 | Color get onPrimaryContainer => _colorScheme.onPrimaryContainer; 17 | 18 | Color get primaryFixed => _colorScheme.primaryFixed; 19 | Color get primaryFixedDim => _colorScheme.primaryFixedDim; 20 | Color get onPrimaryFixed => _colorScheme.onPrimaryFixed; 21 | Color get onPrimaryFixedVariant => _colorScheme.onPrimaryFixedVariant; 22 | 23 | Color get secondary => _colorScheme.secondary; 24 | Color get onSecondary => _colorScheme.onSecondary; 25 | Color get secondaryContainer => _colorScheme.secondaryContainer; 26 | Color get onSecondaryContainer => _colorScheme.onSecondaryContainer; 27 | 28 | Color get secondaryFixed => _colorScheme.secondaryFixed; 29 | Color get secondaryFixedDim => _colorScheme.secondaryFixedDim; 30 | Color get onSecondaryFixed => _colorScheme.onSecondaryFixed; 31 | Color get onSecondaryFixedVariant => _colorScheme.onSecondaryFixedVariant; 32 | 33 | Color get tertiary => _colorScheme.tertiary; 34 | Color get onTertiary => _colorScheme.onTertiary; 35 | Color get tertiaryContainer => _colorScheme.tertiaryContainer; 36 | Color get onTertiaryContainer => _colorScheme.onTertiaryContainer; 37 | 38 | Color get tertiaryFixed => _colorScheme.tertiaryFixed; 39 | Color get tertiaryFixedDim => _colorScheme.tertiaryFixedDim; 40 | Color get onTertiaryFixed => _colorScheme.onTertiaryFixed; 41 | Color get onTertiaryFixedVariant => _colorScheme.onTertiaryFixedVariant; 42 | 43 | Color get error => _colorScheme.error; 44 | Color get onError => _colorScheme.onError; 45 | Color get errorContainer => _colorScheme.errorContainer; 46 | Color get onErrorContainer => _colorScheme.onErrorContainer; 47 | 48 | Color get surface => _colorScheme.surface; 49 | Color get onSurface => _colorScheme.onSurface; 50 | Color get surfaceDim => _colorScheme.surfaceDim; 51 | Color get surfaceBright => _colorScheme.surfaceBright; 52 | Color get surfaceContainerLowest => _colorScheme.surfaceContainerLowest; 53 | Color get surfaceContainerLow => _colorScheme.surfaceContainerLow; 54 | Color get surfaceContainer => _colorScheme.surfaceContainer; 55 | Color get surfaceContainerHigh => _colorScheme.surfaceContainerHigh; 56 | Color get surfaceContainerHighest => _colorScheme.surfaceContainerHighest; 57 | Color get onSurfaceVariant => _colorScheme.onSurfaceVariant; 58 | 59 | Color get outline => _colorScheme.outline; 60 | Color get outlineVariant => _colorScheme.outlineVariant; 61 | Color get shadow => _colorScheme.shadow; 62 | Color get scrim => _colorScheme.scrim; 63 | Color get inverseSurface => _colorScheme.inverseSurface; 64 | Color get onInverseSurface => _colorScheme.onInverseSurface; 65 | Color get inversePrimary => _colorScheme.inversePrimary; 66 | Color get surfaceTint => _colorScheme.surfaceTint; 67 | 68 | /// テキストテーマを返す 69 | TextTheme get _textTheme => Theme.of(this).textTheme; 70 | TextStyle? get displayLarge => _textTheme.displayLarge; 71 | TextStyle? get displayMedium => _textTheme.displayMedium; 72 | TextStyle? get displaySmall => _textTheme.displaySmall; 73 | TextStyle? get headlineLarge => _textTheme.headlineLarge; 74 | TextStyle? get headlineMedium => _textTheme.headlineMedium; 75 | TextStyle? get headlineSmall => _textTheme.headlineSmall; 76 | TextStyle? get titleLarge => _textTheme.titleLarge; 77 | TextStyle? get titleMedium => _textTheme.titleMedium; 78 | TextStyle? get titleSmall => _textTheme.titleSmall; 79 | TextStyle? get labelLarge => _textTheme.labelLarge; 80 | TextStyle? get labelMedium => _textTheme.labelMedium; 81 | TextStyle? get labelSmall => _textTheme.labelSmall; 82 | TextStyle? get bodyLarge => _textTheme.bodyLarge; 83 | TextStyle? get bodyMedium => _textTheme.bodyMedium; 84 | TextStyle? get bodySmall => _textTheme.bodySmall; 85 | TextStyle? get caption => labelSmall?.copyWith(color: outline); 86 | } 87 | 88 | extension CorePaletteX on CorePalette { 89 | MaterialColor get primaryMaterial => primary.toMaterialColor(); 90 | MaterialColor get secondaryMaterial => secondary.toMaterialColor(); 91 | MaterialColor get tertiaryMaterial => tertiary.toMaterialColor(); 92 | MaterialColor get errorMaterial => error.toMaterialColor(); 93 | MaterialColor get neutralMaterial => neutral.toMaterialColor(); 94 | MaterialColor get neutralVariantMaterial => neutralVariant.toMaterialColor(); 95 | } 96 | 97 | extension TonalPaletteX on TonalPalette { 98 | /// MaterialColorに変換する 99 | MaterialColor toMaterialColor() { 100 | final primary = get(40); 101 | return MaterialColor(primary, { 102 | 1: Color(get(99)), 103 | 50: Color(get(95)), 104 | 100: Color(get(90)), 105 | 200: Color(get(80)), 106 | 300: Color(get(70)), 107 | 400: Color(get(60)), 108 | 500: Color(get(50)), 109 | 600: Color(primary), 110 | 700: Color(get(30)), 111 | 800: Color(get(20)), 112 | 900: Color(get(10)), 113 | }); 114 | } 115 | } 116 | 117 | extension MaterialColorX on MaterialColor { 118 | /// 標準のMaterialColorで一番明るいのはshade50だが、それよりももっと明るく 119 | /// ほぼ白色だがほんの少しだけ色がついている色 120 | Color? get shade1 => this[1]; 121 | } 122 | 123 | extension ColorX on Color { 124 | /// HEX型の文字列に変換する 125 | String toHexString() { 126 | return '${red.toHexString()}' 127 | '${green.toHexString()}' 128 | '${blue.toHexString()}'; 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /lib/core/ui/component/messenger.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 3 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 4 | 5 | part 'messenger.g.dart'; 6 | 7 | @riverpod 8 | GlobalKey scaffoldMessengerKey( 9 | Ref ref, 10 | ) { 11 | return GlobalKey(); 12 | } 13 | 14 | @riverpod 15 | GlobalKey navigatorKey(Ref ref) { 16 | return GlobalKey(); 17 | } 18 | 19 | /// エラーダイアログ 20 | class ErrorDialog extends StatelessWidget { 21 | const ErrorDialog({ 22 | super.key, 23 | required this.error, 24 | }); 25 | 26 | final Object error; 27 | 28 | @override 29 | Widget build(BuildContext context) { 30 | return AlertDialog( 31 | title: const Text('エラー'), 32 | content: Text(error.toString()), 33 | actions: [ 34 | TextButton( 35 | onPressed: () => Navigator.of(context).pop(), 36 | child: const Text('OK'), 37 | ), 38 | ], 39 | ); 40 | } 41 | } 42 | 43 | /// 確認ダイアログ 44 | class ConfirmDialog extends ConsumerWidget { 45 | const ConfirmDialog({ 46 | super.key, 47 | required this.content, 48 | required this.onApproved, 49 | }); 50 | 51 | final Widget content; 52 | final VoidCallback? onApproved; 53 | 54 | @override 55 | Widget build(BuildContext context, WidgetRef ref) { 56 | return AlertDialog( 57 | title: const Text('確認'), 58 | content: content, 59 | actions: [ 60 | TextButton( 61 | onPressed: () => Navigator.of(context).pop(), 62 | child: const Text('いいえ'), 63 | ), 64 | TextButton( 65 | onPressed: () { 66 | Navigator.of(context).pop(); 67 | onApproved?.call(); 68 | }, 69 | child: const Text('はい'), 70 | ), 71 | ], 72 | ); 73 | } 74 | } 75 | 76 | /// メッセージダイアログ 77 | class MessageDialog extends StatelessWidget { 78 | const MessageDialog({ 79 | super.key, 80 | this.title, 81 | this.content, 82 | }); 83 | 84 | final Widget? title; 85 | final Widget? content; 86 | 87 | @override 88 | Widget build(BuildContext context) { 89 | return AlertDialog( 90 | title: title, 91 | content: content, 92 | actions: [ 93 | TextButton( 94 | onPressed: () => Navigator.of(context).pop(), 95 | child: const Text('OK'), 96 | ), 97 | ], 98 | ); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /lib/core/ui/component/messenger.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'messenger.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$scaffoldMessengerKeyHash() => 10 | r'a1bded3ff8d4a42a7c5e2ae7a7f744477c25872f'; 11 | 12 | /// See also [scaffoldMessengerKey]. 13 | @ProviderFor(scaffoldMessengerKey) 14 | final scaffoldMessengerKeyProvider = 15 | AutoDisposeProvider>.internal( 16 | scaffoldMessengerKey, 17 | name: r'scaffoldMessengerKeyProvider', 18 | debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') 19 | ? null 20 | : _$scaffoldMessengerKeyHash, 21 | dependencies: null, 22 | allTransitiveDependencies: null, 23 | ); 24 | 25 | @Deprecated('Will be removed in 3.0. Use Ref instead') 26 | // ignore: unused_element 27 | typedef ScaffoldMessengerKeyRef 28 | = AutoDisposeProviderRef>; 29 | String _$navigatorKeyHash() => r'6e9d7249fe86101ea2092a8617d10993370d5975'; 30 | 31 | /// See also [navigatorKey]. 32 | @ProviderFor(navigatorKey) 33 | final navigatorKeyProvider = 34 | AutoDisposeProvider>.internal( 35 | navigatorKey, 36 | name: r'navigatorKeyProvider', 37 | debugGetCreateSourceHash: 38 | const bool.fromEnvironment('dart.vm.product') ? null : _$navigatorKeyHash, 39 | dependencies: null, 40 | allTransitiveDependencies: null, 41 | ); 42 | 43 | @Deprecated('Will be removed in 3.0. Use Ref instead') 44 | // ignore: unused_element 45 | typedef NavigatorKeyRef = AutoDisposeProviderRef>; 46 | // ignore_for_file: type=lint 47 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package 48 | -------------------------------------------------------------------------------- /lib/core/ui/component/responsive.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:responsive_framework/responsive_framework.dart'; 3 | 4 | /// レスポンシブ 5 | class Responsive extends StatelessWidget { 6 | const Responsive({ 7 | super.key, 8 | required this.mobile, 9 | this.tablet, 10 | this.desktop, 11 | }); 12 | 13 | final Widget mobile; 14 | final Widget? tablet; 15 | final Widget? desktop; 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return ResponsiveValue( 20 | context, 21 | defaultValue: mobile, 22 | valueWhen: [ 23 | if (tablet != null) 24 | Condition.largerThan( 25 | name: MOBILE, 26 | value: tablet, 27 | ), 28 | if (desktop != null) 29 | Condition.largerThan( 30 | name: TABLET, 31 | value: desktop, 32 | ), 33 | ], 34 | ).value!; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/core/ui/component/widget_ref_x.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart' as material; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 4 | 5 | import 'messenger.dart'; 6 | 7 | extension WidgetRefX on WidgetRef { 8 | ScaffoldMessengerState? get _currentState => 9 | read(scaffoldMessengerKeyProvider).currentState; 10 | 11 | ScaffoldFeatureController? showSnackBar( 12 | SnackBar snackBar, 13 | ) { 14 | return _currentState?.showSnackBar(snackBar); 15 | } 16 | 17 | void hideCurrentSnackBar({ 18 | SnackBarClosedReason reason = SnackBarClosedReason.hide, 19 | }) { 20 | _currentState?.hideCurrentSnackBar(reason: reason); 21 | } 22 | 23 | Future showDialog({ 24 | required Widget child, 25 | bool barrierDismissible = true, 26 | Color? barrierColor = Colors.black54, 27 | String? barrierLabel, 28 | bool useSafeArea = true, 29 | bool useRootNavigator = true, 30 | RouteSettings? routeSettings, 31 | Offset? anchorPoint, 32 | }) async { 33 | return material.showDialog( 34 | context: context, 35 | builder: (_) => child, 36 | barrierDismissible: barrierDismissible, 37 | barrierColor: barrierColor, 38 | barrierLabel: barrierLabel, 39 | useSafeArea: useSafeArea, 40 | useRootNavigator: useRootNavigator, 41 | routeSettings: routeSettings, 42 | anchorPoint: anchorPoint, 43 | ); 44 | } 45 | 46 | Future showErrorDialog({ 47 | required Object error, 48 | bool barrierDismissible = true, 49 | Color? barrierColor = Colors.black54, 50 | String? barrierLabel, 51 | bool useSafeArea = true, 52 | bool useRootNavigator = true, 53 | RouteSettings? routeSettings, 54 | Offset? anchorPoint, 55 | }) async { 56 | return material.showDialog( 57 | context: context, 58 | builder: (context) => ErrorDialog(error: error), 59 | barrierDismissible: barrierDismissible, 60 | barrierColor: barrierColor, 61 | barrierLabel: barrierLabel, 62 | useSafeArea: useSafeArea, 63 | useRootNavigator: useRootNavigator, 64 | routeSettings: routeSettings, 65 | anchorPoint: anchorPoint, 66 | ); 67 | } 68 | 69 | void listenAsync( 70 | ProviderListenable> provider, { 71 | void Function(T data)? success, 72 | void Function(Object error, StackTrace stackTrace)? error, 73 | void Function()? loading, 74 | }) { 75 | listen>( 76 | provider, 77 | (prev, next) async { 78 | await next.when( 79 | data: (data) { 80 | success?.call(data); 81 | }, 82 | error: (err, stack) async { 83 | await showErrorDialog(error: err); 84 | error?.call(err, stack); 85 | }, 86 | loading: () { 87 | loading?.call(); 88 | }, 89 | ); 90 | }, 91 | onError: (err, stack) async { 92 | await showErrorDialog(error: err); 93 | error?.call(err, stack); 94 | }, 95 | ); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /lib/core/use_case/use_case.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 5 | 6 | // ignore: invalid_use_of_internal_member 7 | mixin UseCase on BuildlessAutoDisposeAsyncNotifier { 8 | bool _mounted = true; 9 | 10 | @protected 11 | void setUnmounted() => _mounted = false; 12 | 13 | @protected 14 | bool get mounted => _mounted; 15 | 16 | @protected 17 | FutureOr buildInternal([FutureOr Function()? future]) { 18 | ref.onDispose(setUnmounted); 19 | return future?.call(); 20 | } 21 | 22 | @protected 23 | Future invokeInternal(Future Function() future) async { 24 | if (state.isLoading) { 25 | return; 26 | } 27 | state = const AsyncValue.loading(); 28 | final newState = await AsyncValue.guard(() async { 29 | return future(); 30 | }); 31 | if (_mounted) { 32 | state = newState; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/feature/app/state/app_version.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | 4 | import '../../../core/data/package_info/package_info.dart'; 5 | 6 | part 'app_version.g.dart'; 7 | 8 | @riverpod 9 | String appVersionText(Ref ref) { 10 | final packageInfo = ref.watch(packageInfoProvider); 11 | return 'v${packageInfo.version}(${packageInfo.buildNumber})'; 12 | } 13 | -------------------------------------------------------------------------------- /lib/feature/app/state/app_version.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'app_version.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$appVersionTextHash() => r'10168db8832b2de4b007874cae571032762bf3ca'; 10 | 11 | /// See also [appVersionText]. 12 | @ProviderFor(appVersionText) 13 | final appVersionTextProvider = AutoDisposeProvider.internal( 14 | appVersionText, 15 | name: r'appVersionTextProvider', 16 | debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') 17 | ? null 18 | : _$appVersionTextHash, 19 | dependencies: null, 20 | allTransitiveDependencies: null, 21 | ); 22 | 23 | @Deprecated('Will be removed in 3.0. Use Ref instead') 24 | // ignore: unused_element 25 | typedef AppVersionTextRef = AutoDisposeProviderRef; 26 | // ignore_for_file: type=lint 27 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package 28 | -------------------------------------------------------------------------------- /lib/feature/app/ui/component/app_version.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 3 | 4 | import '../../../../core/ui/component/material.dart'; 5 | import '../../state/app_version.dart'; 6 | 7 | class AppVersionText extends ConsumerWidget { 8 | const AppVersionText({super.key}); 9 | 10 | @override 11 | Widget build(BuildContext context, WidgetRef ref) { 12 | final text = ref.watch(appVersionTextProvider); 13 | return Padding( 14 | padding: const EdgeInsets.symmetric(vertical: 8), 15 | child: Text( 16 | text, 17 | style: TextStyle( 18 | color: context.outline, 19 | fontSize: 12, 20 | ), 21 | ), 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/feature/app/ui/component/theme.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 3 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 4 | 5 | import '../../../color/state/current_dynamic_scheme_variant.dart'; 6 | import '../../../color/state/current_seed_color.dart'; 7 | 8 | part 'theme.g.dart'; 9 | 10 | @riverpod 11 | ThemeData theme(Ref ref, Brightness brightness) { 12 | final seedColor = ref.watch(currentSeedColorNotifierProvider); 13 | final schemeVariant = ref.watch(currentDynamicSchemeVariantNotifierProvider); 14 | return ThemeData( 15 | useMaterial3: true, 16 | colorScheme: ColorScheme.fromSeed( 17 | seedColor: seedColor, 18 | brightness: brightness, 19 | dynamicSchemeVariant: schemeVariant, 20 | ), 21 | snackBarTheme: const SnackBarThemeData( 22 | behavior: SnackBarBehavior.floating, 23 | ), 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /lib/feature/app/ui/component/theme.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'theme.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$themeHash() => r'5332d2820a8cfd8d708628140b5b072ec0558216'; 10 | 11 | /// Copied from Dart SDK 12 | class _SystemHash { 13 | _SystemHash._(); 14 | 15 | static int combine(int hash, int value) { 16 | // ignore: parameter_assignments 17 | hash = 0x1fffffff & (hash + value); 18 | // ignore: parameter_assignments 19 | hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); 20 | return hash ^ (hash >> 6); 21 | } 22 | 23 | static int finish(int hash) { 24 | // ignore: parameter_assignments 25 | hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); 26 | // ignore: parameter_assignments 27 | hash = hash ^ (hash >> 11); 28 | return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); 29 | } 30 | } 31 | 32 | /// See also [theme]. 33 | @ProviderFor(theme) 34 | const themeProvider = ThemeFamily(); 35 | 36 | /// See also [theme]. 37 | class ThemeFamily extends Family { 38 | /// See also [theme]. 39 | const ThemeFamily(); 40 | 41 | /// See also [theme]. 42 | ThemeProvider call( 43 | Brightness brightness, 44 | ) { 45 | return ThemeProvider( 46 | brightness, 47 | ); 48 | } 49 | 50 | @override 51 | ThemeProvider getProviderOverride( 52 | covariant ThemeProvider provider, 53 | ) { 54 | return call( 55 | provider.brightness, 56 | ); 57 | } 58 | 59 | static const Iterable? _dependencies = null; 60 | 61 | @override 62 | Iterable? get dependencies => _dependencies; 63 | 64 | static const Iterable? _allTransitiveDependencies = null; 65 | 66 | @override 67 | Iterable? get allTransitiveDependencies => 68 | _allTransitiveDependencies; 69 | 70 | @override 71 | String? get name => r'themeProvider'; 72 | } 73 | 74 | /// See also [theme]. 75 | class ThemeProvider extends AutoDisposeProvider { 76 | /// See also [theme]. 77 | ThemeProvider( 78 | Brightness brightness, 79 | ) : this._internal( 80 | (ref) => theme( 81 | ref as ThemeRef, 82 | brightness, 83 | ), 84 | from: themeProvider, 85 | name: r'themeProvider', 86 | debugGetCreateSourceHash: 87 | const bool.fromEnvironment('dart.vm.product') 88 | ? null 89 | : _$themeHash, 90 | dependencies: ThemeFamily._dependencies, 91 | allTransitiveDependencies: ThemeFamily._allTransitiveDependencies, 92 | brightness: brightness, 93 | ); 94 | 95 | ThemeProvider._internal( 96 | super._createNotifier, { 97 | required super.name, 98 | required super.dependencies, 99 | required super.allTransitiveDependencies, 100 | required super.debugGetCreateSourceHash, 101 | required super.from, 102 | required this.brightness, 103 | }) : super.internal(); 104 | 105 | final Brightness brightness; 106 | 107 | @override 108 | Override overrideWith( 109 | ThemeData Function(ThemeRef provider) create, 110 | ) { 111 | return ProviderOverride( 112 | origin: this, 113 | override: ThemeProvider._internal( 114 | (ref) => create(ref as ThemeRef), 115 | from: from, 116 | name: null, 117 | dependencies: null, 118 | allTransitiveDependencies: null, 119 | debugGetCreateSourceHash: null, 120 | brightness: brightness, 121 | ), 122 | ); 123 | } 124 | 125 | @override 126 | AutoDisposeProviderElement createElement() { 127 | return _ThemeProviderElement(this); 128 | } 129 | 130 | @override 131 | bool operator ==(Object other) { 132 | return other is ThemeProvider && other.brightness == brightness; 133 | } 134 | 135 | @override 136 | int get hashCode { 137 | var hash = _SystemHash.combine(0, runtimeType.hashCode); 138 | hash = _SystemHash.combine(hash, brightness.hashCode); 139 | 140 | return _SystemHash.finish(hash); 141 | } 142 | } 143 | 144 | @Deprecated('Will be removed in 3.0. Use Ref instead') 145 | // ignore: unused_element 146 | mixin ThemeRef on AutoDisposeProviderRef { 147 | /// The parameter `brightness` of this provider. 148 | Brightness get brightness; 149 | } 150 | 151 | class _ThemeProviderElement extends AutoDisposeProviderElement 152 | with ThemeRef { 153 | _ThemeProviderElement(super.provider); 154 | 155 | @override 156 | Brightness get brightness => (origin as ThemeProvider).brightness; 157 | } 158 | // ignore_for_file: type=lint 159 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package 160 | -------------------------------------------------------------------------------- /lib/feature/color/state/color_scheme.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 3 | import 'package:freezed_annotation/freezed_annotation.dart'; 4 | import 'package:recase/recase.dart'; 5 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 6 | 7 | import '../../../core/ui/component/material.dart'; 8 | import 'current_color_scheme.dart'; 9 | import 'palette_item.dart'; 10 | 11 | part 'color_scheme.freezed.dart'; 12 | part 'color_scheme.g.dart'; 13 | 14 | @freezed 15 | class ColorSchemeCollection with _$ColorSchemeCollection { 16 | const factory ColorSchemeCollection({ 17 | required List items, 18 | }) = _ColorSchemeCollection; 19 | const ColorSchemeCollection._(); 20 | 21 | PaletteItem getPaletteItem(ColorSchemeKind kind) => 22 | items.firstWhere((item) => item.kind == kind).item; 23 | } 24 | 25 | @freezed 26 | class ColorSchemeItem with _$ColorSchemeItem { 27 | const factory ColorSchemeItem({ 28 | required ColorSchemeKind kind, 29 | required PaletteItem item, 30 | }) = _ColorSchemeItem; 31 | } 32 | 33 | enum ColorSchemeKind { 34 | primary, 35 | onPrimary, 36 | primaryContainer, 37 | onPrimaryContainer, 38 | 39 | primaryFixed, 40 | primaryFixedDim, 41 | onPrimaryFixed, 42 | onPrimaryFixedVariant, 43 | 44 | secondary, 45 | onSecondary, 46 | secondaryContainer, 47 | onSecondaryContainer, 48 | 49 | secondaryFixed, 50 | secondaryFixedDim, 51 | onSecondaryFixed, 52 | onSecondaryFixedVariant, 53 | 54 | tertiary, 55 | onTertiary, 56 | tertiaryContainer, 57 | onTertiaryContainer, 58 | 59 | tertiaryFixed, 60 | tertiaryFixedDim, 61 | onTertiaryFixed, 62 | onTertiaryFixedVariant, 63 | 64 | error, 65 | onError, 66 | errorContainer, 67 | onErrorContainer, 68 | 69 | surface, 70 | onSurface, 71 | surfaceDim, 72 | surfaceBright, 73 | surfaceContainerLowest, 74 | surfaceContainerLow, 75 | surfaceContainer, 76 | surfaceContainerHigh, 77 | surfaceContainerHighest, 78 | onSurfaceVariant, 79 | 80 | outline, 81 | outlineVariant, 82 | shadow, 83 | scrim, 84 | inverseSurface, 85 | onInverseSurface, 86 | inversePrimary, 87 | surfaceTint, 88 | ; 89 | 90 | String get title => ReCase(name).titleCase; 91 | } 92 | 93 | @riverpod 94 | ColorSchemeCollection colorSchemeCollection( 95 | Ref ref, 96 | ) { 97 | final colorScheme = ref.watch(currentColorSchemeProvider); 98 | return ColorSchemeCollection( 99 | items: ColorSchemeKind.values 100 | .map( 101 | (kind) => ColorSchemeItem( 102 | kind: kind, 103 | item: PaletteItem( 104 | backgroundColor: colorScheme.getColor(kind), 105 | foregroundColor: colorScheme.getOnColor(kind), 106 | text: kind.title, 107 | subText: colorScheme.getColor(kind).toHexString(), 108 | ), 109 | ), 110 | ) 111 | .toList(), 112 | ); 113 | } 114 | 115 | extension on ColorScheme { 116 | Color getColor(ColorSchemeKind kind) => switch (kind) { 117 | ColorSchemeKind.primary => primary, 118 | ColorSchemeKind.onPrimary => onPrimary, 119 | ColorSchemeKind.primaryContainer => primaryContainer, 120 | ColorSchemeKind.onPrimaryContainer => onPrimaryContainer, 121 | ColorSchemeKind.primaryFixed => primaryFixed, 122 | ColorSchemeKind.primaryFixedDim => primaryFixedDim, 123 | ColorSchemeKind.onPrimaryFixed => onPrimaryFixed, 124 | ColorSchemeKind.onPrimaryFixedVariant => onPrimaryFixedVariant, 125 | ColorSchemeKind.secondary => secondary, 126 | ColorSchemeKind.onSecondary => onSecondary, 127 | ColorSchemeKind.secondaryContainer => secondaryContainer, 128 | ColorSchemeKind.onSecondaryContainer => onSecondaryContainer, 129 | ColorSchemeKind.secondaryFixed => secondaryFixed, 130 | ColorSchemeKind.secondaryFixedDim => secondaryFixedDim, 131 | ColorSchemeKind.onSecondaryFixed => onSecondaryFixed, 132 | ColorSchemeKind.onSecondaryFixedVariant => onSecondaryFixedVariant, 133 | ColorSchemeKind.tertiary => tertiary, 134 | ColorSchemeKind.onTertiary => onTertiary, 135 | ColorSchemeKind.tertiaryContainer => tertiaryContainer, 136 | ColorSchemeKind.onTertiaryContainer => onTertiaryContainer, 137 | ColorSchemeKind.tertiaryFixed => tertiaryFixed, 138 | ColorSchemeKind.tertiaryFixedDim => tertiaryFixedDim, 139 | ColorSchemeKind.onTertiaryFixed => onTertiaryFixed, 140 | ColorSchemeKind.onTertiaryFixedVariant => onTertiaryFixedVariant, 141 | ColorSchemeKind.error => error, 142 | ColorSchemeKind.onError => onError, 143 | ColorSchemeKind.errorContainer => errorContainer, 144 | ColorSchemeKind.onErrorContainer => onErrorContainer, 145 | ColorSchemeKind.surface => surface, 146 | ColorSchemeKind.onSurface => onSurface, 147 | ColorSchemeKind.surfaceDim => surfaceDim, 148 | ColorSchemeKind.surfaceBright => surfaceBright, 149 | ColorSchemeKind.surfaceContainerLowest => surfaceContainerLowest, 150 | ColorSchemeKind.surfaceContainerLow => surfaceContainerLow, 151 | ColorSchemeKind.surfaceContainer => surfaceContainer, 152 | ColorSchemeKind.surfaceContainerHigh => surfaceContainerHigh, 153 | ColorSchemeKind.surfaceContainerHighest => surfaceContainerHighest, 154 | ColorSchemeKind.onSurfaceVariant => onSurfaceVariant, 155 | ColorSchemeKind.outline => outline, 156 | ColorSchemeKind.outlineVariant => outlineVariant, 157 | ColorSchemeKind.shadow => shadow, 158 | ColorSchemeKind.scrim => scrim, 159 | ColorSchemeKind.inverseSurface => inverseSurface, 160 | ColorSchemeKind.onInverseSurface => onInverseSurface, 161 | ColorSchemeKind.inversePrimary => inversePrimary, 162 | ColorSchemeKind.surfaceTint => surfaceTint, 163 | }; 164 | 165 | Color getOnColor(ColorSchemeKind kind) => switch (kind) { 166 | ColorSchemeKind.primary => onPrimary, 167 | ColorSchemeKind.onPrimary => primary, 168 | ColorSchemeKind.primaryContainer => onPrimaryContainer, 169 | ColorSchemeKind.onPrimaryContainer => primaryContainer, 170 | ColorSchemeKind.primaryFixed => onPrimaryFixed, 171 | ColorSchemeKind.primaryFixedDim => onPrimaryFixed, 172 | ColorSchemeKind.onPrimaryFixed => primaryFixed, 173 | ColorSchemeKind.onPrimaryFixedVariant => primaryFixed, 174 | ColorSchemeKind.secondary => onSecondary, 175 | ColorSchemeKind.onSecondary => secondary, 176 | ColorSchemeKind.secondaryContainer => onSecondaryContainer, 177 | ColorSchemeKind.onSecondaryContainer => secondaryContainer, 178 | ColorSchemeKind.secondaryFixed => onSecondaryFixed, 179 | ColorSchemeKind.secondaryFixedDim => onSecondaryFixed, 180 | ColorSchemeKind.onSecondaryFixed => secondaryFixed, 181 | ColorSchemeKind.onSecondaryFixedVariant => secondaryFixed, 182 | ColorSchemeKind.tertiary => onTertiary, 183 | ColorSchemeKind.onTertiary => tertiary, 184 | ColorSchemeKind.tertiaryContainer => onTertiaryContainer, 185 | ColorSchemeKind.onTertiaryContainer => tertiaryContainer, 186 | ColorSchemeKind.tertiaryFixed => onTertiaryFixed, 187 | ColorSchemeKind.tertiaryFixedDim => onTertiaryFixed, 188 | ColorSchemeKind.onTertiaryFixed => tertiaryFixed, 189 | ColorSchemeKind.onTertiaryFixedVariant => tertiaryFixed, 190 | ColorSchemeKind.error => onError, 191 | ColorSchemeKind.onError => error, 192 | ColorSchemeKind.errorContainer => onErrorContainer, 193 | ColorSchemeKind.onErrorContainer => errorContainer, 194 | ColorSchemeKind.surface => onSurface, 195 | ColorSchemeKind.onSurface => surface, 196 | ColorSchemeKind.surfaceDim => onSurface, 197 | ColorSchemeKind.surfaceBright => onSurface, 198 | ColorSchemeKind.surfaceContainerLowest => onSurface, 199 | ColorSchemeKind.surfaceContainerLow => onSurface, 200 | ColorSchemeKind.surfaceContainer => onSurface, 201 | ColorSchemeKind.surfaceContainerHigh => onSurface, 202 | ColorSchemeKind.surfaceContainerHighest => onSurface, 203 | ColorSchemeKind.onSurfaceVariant => surface, 204 | ColorSchemeKind.outline => surface, 205 | ColorSchemeKind.outlineVariant => onSurface, 206 | ColorSchemeKind.shadow => Colors.white, 207 | ColorSchemeKind.scrim => Colors.white, 208 | ColorSchemeKind.inverseSurface => onInverseSurface, 209 | ColorSchemeKind.onInverseSurface => inverseSurface, 210 | ColorSchemeKind.inversePrimary => onSurface, 211 | ColorSchemeKind.surfaceTint => surfaceTint, 212 | }; 213 | } 214 | -------------------------------------------------------------------------------- /lib/feature/color/state/color_scheme.freezed.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | // GENERATED CODE - DO NOT MODIFY BY HAND 3 | // ignore_for_file: type=lint 4 | // 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 5 | 6 | part of 'color_scheme.dart'; 7 | 8 | // ************************************************************************** 9 | // FreezedGenerator 10 | // ************************************************************************** 11 | 12 | T _$identity(T value) => value; 13 | 14 | final _privateConstructorUsedError = UnsupportedError( 15 | '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#adding-getters-and-methods-to-our-models'); 16 | 17 | /// @nodoc 18 | mixin _$ColorSchemeCollection { 19 | List get items => throw _privateConstructorUsedError; 20 | 21 | /// Create a copy of ColorSchemeCollection 22 | /// with the given fields replaced by the non-null parameter values. 23 | @JsonKey(includeFromJson: false, includeToJson: false) 24 | $ColorSchemeCollectionCopyWith get copyWith => 25 | throw _privateConstructorUsedError; 26 | } 27 | 28 | /// @nodoc 29 | abstract class $ColorSchemeCollectionCopyWith<$Res> { 30 | factory $ColorSchemeCollectionCopyWith(ColorSchemeCollection value, 31 | $Res Function(ColorSchemeCollection) then) = 32 | _$ColorSchemeCollectionCopyWithImpl<$Res, ColorSchemeCollection>; 33 | @useResult 34 | $Res call({List items}); 35 | } 36 | 37 | /// @nodoc 38 | class _$ColorSchemeCollectionCopyWithImpl<$Res, 39 | $Val extends ColorSchemeCollection> 40 | implements $ColorSchemeCollectionCopyWith<$Res> { 41 | _$ColorSchemeCollectionCopyWithImpl(this._value, this._then); 42 | 43 | // ignore: unused_field 44 | final $Val _value; 45 | // ignore: unused_field 46 | final $Res Function($Val) _then; 47 | 48 | /// Create a copy of ColorSchemeCollection 49 | /// with the given fields replaced by the non-null parameter values. 50 | @pragma('vm:prefer-inline') 51 | @override 52 | $Res call({ 53 | Object? items = null, 54 | }) { 55 | return _then(_value.copyWith( 56 | items: null == items 57 | ? _value.items 58 | : items // ignore: cast_nullable_to_non_nullable 59 | as List, 60 | ) as $Val); 61 | } 62 | } 63 | 64 | /// @nodoc 65 | abstract class _$$ColorSchemeCollectionImplCopyWith<$Res> 66 | implements $ColorSchemeCollectionCopyWith<$Res> { 67 | factory _$$ColorSchemeCollectionImplCopyWith( 68 | _$ColorSchemeCollectionImpl value, 69 | $Res Function(_$ColorSchemeCollectionImpl) then) = 70 | __$$ColorSchemeCollectionImplCopyWithImpl<$Res>; 71 | @override 72 | @useResult 73 | $Res call({List items}); 74 | } 75 | 76 | /// @nodoc 77 | class __$$ColorSchemeCollectionImplCopyWithImpl<$Res> 78 | extends _$ColorSchemeCollectionCopyWithImpl<$Res, 79 | _$ColorSchemeCollectionImpl> 80 | implements _$$ColorSchemeCollectionImplCopyWith<$Res> { 81 | __$$ColorSchemeCollectionImplCopyWithImpl(_$ColorSchemeCollectionImpl _value, 82 | $Res Function(_$ColorSchemeCollectionImpl) _then) 83 | : super(_value, _then); 84 | 85 | /// Create a copy of ColorSchemeCollection 86 | /// with the given fields replaced by the non-null parameter values. 87 | @pragma('vm:prefer-inline') 88 | @override 89 | $Res call({ 90 | Object? items = null, 91 | }) { 92 | return _then(_$ColorSchemeCollectionImpl( 93 | items: null == items 94 | ? _value._items 95 | : items // ignore: cast_nullable_to_non_nullable 96 | as List, 97 | )); 98 | } 99 | } 100 | 101 | /// @nodoc 102 | 103 | class _$ColorSchemeCollectionImpl extends _ColorSchemeCollection { 104 | const _$ColorSchemeCollectionImpl( 105 | {required final List items}) 106 | : _items = items, 107 | super._(); 108 | 109 | final List _items; 110 | @override 111 | List get items { 112 | if (_items is EqualUnmodifiableListView) return _items; 113 | // ignore: implicit_dynamic_type 114 | return EqualUnmodifiableListView(_items); 115 | } 116 | 117 | @override 118 | String toString() { 119 | return 'ColorSchemeCollection(items: $items)'; 120 | } 121 | 122 | @override 123 | bool operator ==(Object other) { 124 | return identical(this, other) || 125 | (other.runtimeType == runtimeType && 126 | other is _$ColorSchemeCollectionImpl && 127 | const DeepCollectionEquality().equals(other._items, _items)); 128 | } 129 | 130 | @override 131 | int get hashCode => 132 | Object.hash(runtimeType, const DeepCollectionEquality().hash(_items)); 133 | 134 | /// Create a copy of ColorSchemeCollection 135 | /// with the given fields replaced by the non-null parameter values. 136 | @JsonKey(includeFromJson: false, includeToJson: false) 137 | @override 138 | @pragma('vm:prefer-inline') 139 | _$$ColorSchemeCollectionImplCopyWith<_$ColorSchemeCollectionImpl> 140 | get copyWith => __$$ColorSchemeCollectionImplCopyWithImpl< 141 | _$ColorSchemeCollectionImpl>(this, _$identity); 142 | } 143 | 144 | abstract class _ColorSchemeCollection extends ColorSchemeCollection { 145 | const factory _ColorSchemeCollection( 146 | {required final List items}) = 147 | _$ColorSchemeCollectionImpl; 148 | const _ColorSchemeCollection._() : super._(); 149 | 150 | @override 151 | List get items; 152 | 153 | /// Create a copy of ColorSchemeCollection 154 | /// with the given fields replaced by the non-null parameter values. 155 | @override 156 | @JsonKey(includeFromJson: false, includeToJson: false) 157 | _$$ColorSchemeCollectionImplCopyWith<_$ColorSchemeCollectionImpl> 158 | get copyWith => throw _privateConstructorUsedError; 159 | } 160 | 161 | /// @nodoc 162 | mixin _$ColorSchemeItem { 163 | ColorSchemeKind get kind => throw _privateConstructorUsedError; 164 | PaletteItem get item => throw _privateConstructorUsedError; 165 | 166 | /// Create a copy of ColorSchemeItem 167 | /// with the given fields replaced by the non-null parameter values. 168 | @JsonKey(includeFromJson: false, includeToJson: false) 169 | $ColorSchemeItemCopyWith get copyWith => 170 | throw _privateConstructorUsedError; 171 | } 172 | 173 | /// @nodoc 174 | abstract class $ColorSchemeItemCopyWith<$Res> { 175 | factory $ColorSchemeItemCopyWith( 176 | ColorSchemeItem value, $Res Function(ColorSchemeItem) then) = 177 | _$ColorSchemeItemCopyWithImpl<$Res, ColorSchemeItem>; 178 | @useResult 179 | $Res call({ColorSchemeKind kind, PaletteItem item}); 180 | 181 | $PaletteItemCopyWith<$Res> get item; 182 | } 183 | 184 | /// @nodoc 185 | class _$ColorSchemeItemCopyWithImpl<$Res, $Val extends ColorSchemeItem> 186 | implements $ColorSchemeItemCopyWith<$Res> { 187 | _$ColorSchemeItemCopyWithImpl(this._value, this._then); 188 | 189 | // ignore: unused_field 190 | final $Val _value; 191 | // ignore: unused_field 192 | final $Res Function($Val) _then; 193 | 194 | /// Create a copy of ColorSchemeItem 195 | /// with the given fields replaced by the non-null parameter values. 196 | @pragma('vm:prefer-inline') 197 | @override 198 | $Res call({ 199 | Object? kind = null, 200 | Object? item = null, 201 | }) { 202 | return _then(_value.copyWith( 203 | kind: null == kind 204 | ? _value.kind 205 | : kind // ignore: cast_nullable_to_non_nullable 206 | as ColorSchemeKind, 207 | item: null == item 208 | ? _value.item 209 | : item // ignore: cast_nullable_to_non_nullable 210 | as PaletteItem, 211 | ) as $Val); 212 | } 213 | 214 | /// Create a copy of ColorSchemeItem 215 | /// with the given fields replaced by the non-null parameter values. 216 | @override 217 | @pragma('vm:prefer-inline') 218 | $PaletteItemCopyWith<$Res> get item { 219 | return $PaletteItemCopyWith<$Res>(_value.item, (value) { 220 | return _then(_value.copyWith(item: value) as $Val); 221 | }); 222 | } 223 | } 224 | 225 | /// @nodoc 226 | abstract class _$$ColorSchemeItemImplCopyWith<$Res> 227 | implements $ColorSchemeItemCopyWith<$Res> { 228 | factory _$$ColorSchemeItemImplCopyWith(_$ColorSchemeItemImpl value, 229 | $Res Function(_$ColorSchemeItemImpl) then) = 230 | __$$ColorSchemeItemImplCopyWithImpl<$Res>; 231 | @override 232 | @useResult 233 | $Res call({ColorSchemeKind kind, PaletteItem item}); 234 | 235 | @override 236 | $PaletteItemCopyWith<$Res> get item; 237 | } 238 | 239 | /// @nodoc 240 | class __$$ColorSchemeItemImplCopyWithImpl<$Res> 241 | extends _$ColorSchemeItemCopyWithImpl<$Res, _$ColorSchemeItemImpl> 242 | implements _$$ColorSchemeItemImplCopyWith<$Res> { 243 | __$$ColorSchemeItemImplCopyWithImpl( 244 | _$ColorSchemeItemImpl _value, $Res Function(_$ColorSchemeItemImpl) _then) 245 | : super(_value, _then); 246 | 247 | /// Create a copy of ColorSchemeItem 248 | /// with the given fields replaced by the non-null parameter values. 249 | @pragma('vm:prefer-inline') 250 | @override 251 | $Res call({ 252 | Object? kind = null, 253 | Object? item = null, 254 | }) { 255 | return _then(_$ColorSchemeItemImpl( 256 | kind: null == kind 257 | ? _value.kind 258 | : kind // ignore: cast_nullable_to_non_nullable 259 | as ColorSchemeKind, 260 | item: null == item 261 | ? _value.item 262 | : item // ignore: cast_nullable_to_non_nullable 263 | as PaletteItem, 264 | )); 265 | } 266 | } 267 | 268 | /// @nodoc 269 | 270 | class _$ColorSchemeItemImpl implements _ColorSchemeItem { 271 | const _$ColorSchemeItemImpl({required this.kind, required this.item}); 272 | 273 | @override 274 | final ColorSchemeKind kind; 275 | @override 276 | final PaletteItem item; 277 | 278 | @override 279 | String toString() { 280 | return 'ColorSchemeItem(kind: $kind, item: $item)'; 281 | } 282 | 283 | @override 284 | bool operator ==(Object other) { 285 | return identical(this, other) || 286 | (other.runtimeType == runtimeType && 287 | other is _$ColorSchemeItemImpl && 288 | (identical(other.kind, kind) || other.kind == kind) && 289 | (identical(other.item, item) || other.item == item)); 290 | } 291 | 292 | @override 293 | int get hashCode => Object.hash(runtimeType, kind, item); 294 | 295 | /// Create a copy of ColorSchemeItem 296 | /// with the given fields replaced by the non-null parameter values. 297 | @JsonKey(includeFromJson: false, includeToJson: false) 298 | @override 299 | @pragma('vm:prefer-inline') 300 | _$$ColorSchemeItemImplCopyWith<_$ColorSchemeItemImpl> get copyWith => 301 | __$$ColorSchemeItemImplCopyWithImpl<_$ColorSchemeItemImpl>( 302 | this, _$identity); 303 | } 304 | 305 | abstract class _ColorSchemeItem implements ColorSchemeItem { 306 | const factory _ColorSchemeItem( 307 | {required final ColorSchemeKind kind, 308 | required final PaletteItem item}) = _$ColorSchemeItemImpl; 309 | 310 | @override 311 | ColorSchemeKind get kind; 312 | @override 313 | PaletteItem get item; 314 | 315 | /// Create a copy of ColorSchemeItem 316 | /// with the given fields replaced by the non-null parameter values. 317 | @override 318 | @JsonKey(includeFromJson: false, includeToJson: false) 319 | _$$ColorSchemeItemImplCopyWith<_$ColorSchemeItemImpl> get copyWith => 320 | throw _privateConstructorUsedError; 321 | } 322 | -------------------------------------------------------------------------------- /lib/feature/color/state/color_scheme.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'color_scheme.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$colorSchemeCollectionHash() => 10 | r'71e98971b8f0f182d6193dc12a2e620305957b97'; 11 | 12 | /// See also [colorSchemeCollection]. 13 | @ProviderFor(colorSchemeCollection) 14 | final colorSchemeCollectionProvider = 15 | AutoDisposeProvider.internal( 16 | colorSchemeCollection, 17 | name: r'colorSchemeCollectionProvider', 18 | debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') 19 | ? null 20 | : _$colorSchemeCollectionHash, 21 | dependencies: null, 22 | allTransitiveDependencies: null, 23 | ); 24 | 25 | @Deprecated('Will be removed in 3.0. Use Ref instead') 26 | // ignore: unused_element 27 | typedef ColorSchemeCollectionRef 28 | = AutoDisposeProviderRef; 29 | // ignore_for_file: type=lint 30 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package 31 | -------------------------------------------------------------------------------- /lib/feature/color/state/current_color_scheme.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 3 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 4 | 5 | import '../../theme_mode/state/current_brightness.dart'; 6 | import 'current_dynamic_scheme_variant.dart'; 7 | import 'current_seed_color.dart'; 8 | 9 | part 'current_color_scheme.g.dart'; 10 | 11 | @riverpod 12 | ColorScheme currentColorScheme(Ref ref) { 13 | final seedColor = ref.watch(currentSeedColorNotifierProvider); 14 | final brightness = ref.watch(currentBrightnessProvider); 15 | final variant = ref.watch(currentDynamicSchemeVariantNotifierProvider); 16 | return ColorScheme.fromSeed( 17 | seedColor: seedColor, 18 | brightness: brightness, 19 | dynamicSchemeVariant: variant, 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /lib/feature/color/state/current_color_scheme.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'current_color_scheme.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$currentColorSchemeHash() => 10 | r'8f1b2caad35a650c800b7226763d306d4d95fd28'; 11 | 12 | /// See also [currentColorScheme]. 13 | @ProviderFor(currentColorScheme) 14 | final currentColorSchemeProvider = AutoDisposeProvider.internal( 15 | currentColorScheme, 16 | name: r'currentColorSchemeProvider', 17 | debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') 18 | ? null 19 | : _$currentColorSchemeHash, 20 | dependencies: null, 21 | allTransitiveDependencies: null, 22 | ); 23 | 24 | @Deprecated('Will be removed in 3.0. Use Ref instead') 25 | // ignore: unused_element 26 | typedef CurrentColorSchemeRef = AutoDisposeProviderRef; 27 | // ignore_for_file: type=lint 28 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package 29 | -------------------------------------------------------------------------------- /lib/feature/color/state/current_dynamic_scheme_variant.dart: -------------------------------------------------------------------------------- 1 | import 'package:collection/collection.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 4 | 5 | import '../../../core/data/shared_preferences/shared_preferences.dart'; 6 | 7 | part 'current_dynamic_scheme_variant.g.dart'; 8 | 9 | @riverpod 10 | class CurrentDynamicSchemeVariantNotifier 11 | extends _$CurrentDynamicSchemeVariantNotifier { 12 | static const _key = 'dynamicSchemeVariant'; 13 | 14 | @override 15 | DynamicSchemeVariant build() { 16 | final prefs = ref.watch(sharedPreferencesProvider); 17 | return prefs.getString(_key)?.toDynamicSchemeVariant() ?? 18 | DynamicSchemeVariant.rainbow; 19 | } 20 | 21 | Future updateValue(DynamicSchemeVariant value) async { 22 | final prefs = ref.read(sharedPreferencesProvider); 23 | await prefs.setString(_key, value.name); 24 | state = value; 25 | } 26 | } 27 | 28 | extension on String { 29 | DynamicSchemeVariant? toDynamicSchemeVariant() => 30 | DynamicSchemeVariant.values.firstWhereOrNull((e) => e.name == this); 31 | } 32 | -------------------------------------------------------------------------------- /lib/feature/color/state/current_dynamic_scheme_variant.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'current_dynamic_scheme_variant.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$currentDynamicSchemeVariantNotifierHash() => 10 | r'5a1ec7b37c5075b73112335879745cd3ebe0849a'; 11 | 12 | /// See also [CurrentDynamicSchemeVariantNotifier]. 13 | @ProviderFor(CurrentDynamicSchemeVariantNotifier) 14 | final currentDynamicSchemeVariantNotifierProvider = AutoDisposeNotifierProvider< 15 | CurrentDynamicSchemeVariantNotifier, DynamicSchemeVariant>.internal( 16 | CurrentDynamicSchemeVariantNotifier.new, 17 | name: r'currentDynamicSchemeVariantNotifierProvider', 18 | debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') 19 | ? null 20 | : _$currentDynamicSchemeVariantNotifierHash, 21 | dependencies: null, 22 | allTransitiveDependencies: null, 23 | ); 24 | 25 | typedef _$CurrentDynamicSchemeVariantNotifier 26 | = AutoDisposeNotifier; 27 | // ignore_for_file: type=lint 28 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package 29 | -------------------------------------------------------------------------------- /lib/feature/color/state/current_hover_color.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | 4 | part 'current_hover_color.g.dart'; 5 | 6 | @riverpod 7 | class CurrentHoverColor extends _$CurrentHoverColor { 8 | @override 9 | Color? build() => null; 10 | 11 | Future update(Color color) async { 12 | state = color; 13 | } 14 | 15 | void clear() { 16 | state = null; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/feature/color/state/current_hover_color.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'current_hover_color.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$currentHoverColorHash() => r'8dda7515435b3c95e9763c62ff0594fe29fead99'; 10 | 11 | /// See also [CurrentHoverColor]. 12 | @ProviderFor(CurrentHoverColor) 13 | final currentHoverColorProvider = 14 | AutoDisposeNotifierProvider.internal( 15 | CurrentHoverColor.new, 16 | name: r'currentHoverColorProvider', 17 | debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') 18 | ? null 19 | : _$currentHoverColorHash, 20 | dependencies: null, 21 | allTransitiveDependencies: null, 22 | ); 23 | 24 | typedef _$CurrentHoverColor = AutoDisposeNotifier; 25 | // ignore_for_file: type=lint 26 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package 27 | -------------------------------------------------------------------------------- /lib/feature/color/state/current_seed_color.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | 4 | import '../../../core/data/shared_preferences/shared_preferences.dart'; 5 | 6 | part 'current_seed_color.g.dart'; 7 | 8 | @riverpod 9 | class CurrentSeedColorNotifier extends _$CurrentSeedColorNotifier { 10 | static const _key = 'seedColor'; 11 | 12 | @override 13 | Color build() { 14 | final prefs = ref.watch(sharedPreferencesProvider); 15 | return prefs.getInt(_key)?.toColor() ?? const Color(0xFF6750A4); 16 | } 17 | 18 | Future updateValue(Color value) async { 19 | final prefs = ref.read(sharedPreferencesProvider); 20 | await prefs.setInt(_key, value.value); 21 | state = value; 22 | } 23 | } 24 | 25 | extension on int { 26 | Color toColor() => Color(this); 27 | } 28 | -------------------------------------------------------------------------------- /lib/feature/color/state/current_seed_color.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'current_seed_color.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$currentSeedColorNotifierHash() => 10 | r'a6c221a9b9376674851f0d9ad4f9d98a9b4541e1'; 11 | 12 | /// See also [CurrentSeedColorNotifier]. 13 | @ProviderFor(CurrentSeedColorNotifier) 14 | final currentSeedColorNotifierProvider = 15 | AutoDisposeNotifierProvider.internal( 16 | CurrentSeedColorNotifier.new, 17 | name: r'currentSeedColorNotifierProvider', 18 | debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') 19 | ? null 20 | : _$currentSeedColorNotifierHash, 21 | dependencies: null, 22 | allTransitiveDependencies: null, 23 | ); 24 | 25 | typedef _$CurrentSeedColorNotifier = AutoDisposeNotifier; 26 | // ignore_for_file: type=lint 27 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package 28 | -------------------------------------------------------------------------------- /lib/feature/color/state/palette_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'package:freezed_annotation/freezed_annotation.dart'; 4 | 5 | part 'palette_item.freezed.dart'; 6 | 7 | @freezed 8 | class PaletteItem with _$PaletteItem { 9 | const factory PaletteItem({ 10 | required Color backgroundColor, 11 | Color? foregroundColor, 12 | String? text, 13 | String? subText, 14 | }) = _PaletteItem; 15 | } 16 | -------------------------------------------------------------------------------- /lib/feature/color/state/palette_item.freezed.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | // GENERATED CODE - DO NOT MODIFY BY HAND 3 | // ignore_for_file: type=lint 4 | // 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 5 | 6 | part of 'palette_item.dart'; 7 | 8 | // ************************************************************************** 9 | // FreezedGenerator 10 | // ************************************************************************** 11 | 12 | T _$identity(T value) => value; 13 | 14 | final _privateConstructorUsedError = UnsupportedError( 15 | '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#adding-getters-and-methods-to-our-models'); 16 | 17 | /// @nodoc 18 | mixin _$PaletteItem { 19 | Color get backgroundColor => throw _privateConstructorUsedError; 20 | Color? get foregroundColor => throw _privateConstructorUsedError; 21 | String? get text => throw _privateConstructorUsedError; 22 | String? get subText => throw _privateConstructorUsedError; 23 | 24 | /// Create a copy of PaletteItem 25 | /// with the given fields replaced by the non-null parameter values. 26 | @JsonKey(includeFromJson: false, includeToJson: false) 27 | $PaletteItemCopyWith get copyWith => 28 | throw _privateConstructorUsedError; 29 | } 30 | 31 | /// @nodoc 32 | abstract class $PaletteItemCopyWith<$Res> { 33 | factory $PaletteItemCopyWith( 34 | PaletteItem value, $Res Function(PaletteItem) then) = 35 | _$PaletteItemCopyWithImpl<$Res, PaletteItem>; 36 | @useResult 37 | $Res call( 38 | {Color backgroundColor, 39 | Color? foregroundColor, 40 | String? text, 41 | String? subText}); 42 | } 43 | 44 | /// @nodoc 45 | class _$PaletteItemCopyWithImpl<$Res, $Val extends PaletteItem> 46 | implements $PaletteItemCopyWith<$Res> { 47 | _$PaletteItemCopyWithImpl(this._value, this._then); 48 | 49 | // ignore: unused_field 50 | final $Val _value; 51 | // ignore: unused_field 52 | final $Res Function($Val) _then; 53 | 54 | /// Create a copy of PaletteItem 55 | /// with the given fields replaced by the non-null parameter values. 56 | @pragma('vm:prefer-inline') 57 | @override 58 | $Res call({ 59 | Object? backgroundColor = null, 60 | Object? foregroundColor = freezed, 61 | Object? text = freezed, 62 | Object? subText = freezed, 63 | }) { 64 | return _then(_value.copyWith( 65 | backgroundColor: null == backgroundColor 66 | ? _value.backgroundColor 67 | : backgroundColor // ignore: cast_nullable_to_non_nullable 68 | as Color, 69 | foregroundColor: freezed == foregroundColor 70 | ? _value.foregroundColor 71 | : foregroundColor // ignore: cast_nullable_to_non_nullable 72 | as Color?, 73 | text: freezed == text 74 | ? _value.text 75 | : text // ignore: cast_nullable_to_non_nullable 76 | as String?, 77 | subText: freezed == subText 78 | ? _value.subText 79 | : subText // ignore: cast_nullable_to_non_nullable 80 | as String?, 81 | ) as $Val); 82 | } 83 | } 84 | 85 | /// @nodoc 86 | abstract class _$$PaletteItemImplCopyWith<$Res> 87 | implements $PaletteItemCopyWith<$Res> { 88 | factory _$$PaletteItemImplCopyWith( 89 | _$PaletteItemImpl value, $Res Function(_$PaletteItemImpl) then) = 90 | __$$PaletteItemImplCopyWithImpl<$Res>; 91 | @override 92 | @useResult 93 | $Res call( 94 | {Color backgroundColor, 95 | Color? foregroundColor, 96 | String? text, 97 | String? subText}); 98 | } 99 | 100 | /// @nodoc 101 | class __$$PaletteItemImplCopyWithImpl<$Res> 102 | extends _$PaletteItemCopyWithImpl<$Res, _$PaletteItemImpl> 103 | implements _$$PaletteItemImplCopyWith<$Res> { 104 | __$$PaletteItemImplCopyWithImpl( 105 | _$PaletteItemImpl _value, $Res Function(_$PaletteItemImpl) _then) 106 | : super(_value, _then); 107 | 108 | /// Create a copy of PaletteItem 109 | /// with the given fields replaced by the non-null parameter values. 110 | @pragma('vm:prefer-inline') 111 | @override 112 | $Res call({ 113 | Object? backgroundColor = null, 114 | Object? foregroundColor = freezed, 115 | Object? text = freezed, 116 | Object? subText = freezed, 117 | }) { 118 | return _then(_$PaletteItemImpl( 119 | backgroundColor: null == backgroundColor 120 | ? _value.backgroundColor 121 | : backgroundColor // ignore: cast_nullable_to_non_nullable 122 | as Color, 123 | foregroundColor: freezed == foregroundColor 124 | ? _value.foregroundColor 125 | : foregroundColor // ignore: cast_nullable_to_non_nullable 126 | as Color?, 127 | text: freezed == text 128 | ? _value.text 129 | : text // ignore: cast_nullable_to_non_nullable 130 | as String?, 131 | subText: freezed == subText 132 | ? _value.subText 133 | : subText // ignore: cast_nullable_to_non_nullable 134 | as String?, 135 | )); 136 | } 137 | } 138 | 139 | /// @nodoc 140 | 141 | class _$PaletteItemImpl implements _PaletteItem { 142 | const _$PaletteItemImpl( 143 | {required this.backgroundColor, 144 | this.foregroundColor, 145 | this.text, 146 | this.subText}); 147 | 148 | @override 149 | final Color backgroundColor; 150 | @override 151 | final Color? foregroundColor; 152 | @override 153 | final String? text; 154 | @override 155 | final String? subText; 156 | 157 | @override 158 | String toString() { 159 | return 'PaletteItem(backgroundColor: $backgroundColor, foregroundColor: $foregroundColor, text: $text, subText: $subText)'; 160 | } 161 | 162 | @override 163 | bool operator ==(Object other) { 164 | return identical(this, other) || 165 | (other.runtimeType == runtimeType && 166 | other is _$PaletteItemImpl && 167 | (identical(other.backgroundColor, backgroundColor) || 168 | other.backgroundColor == backgroundColor) && 169 | (identical(other.foregroundColor, foregroundColor) || 170 | other.foregroundColor == foregroundColor) && 171 | (identical(other.text, text) || other.text == text) && 172 | (identical(other.subText, subText) || other.subText == subText)); 173 | } 174 | 175 | @override 176 | int get hashCode => 177 | Object.hash(runtimeType, backgroundColor, foregroundColor, text, subText); 178 | 179 | /// Create a copy of PaletteItem 180 | /// with the given fields replaced by the non-null parameter values. 181 | @JsonKey(includeFromJson: false, includeToJson: false) 182 | @override 183 | @pragma('vm:prefer-inline') 184 | _$$PaletteItemImplCopyWith<_$PaletteItemImpl> get copyWith => 185 | __$$PaletteItemImplCopyWithImpl<_$PaletteItemImpl>(this, _$identity); 186 | } 187 | 188 | abstract class _PaletteItem implements PaletteItem { 189 | const factory _PaletteItem( 190 | {required final Color backgroundColor, 191 | final Color? foregroundColor, 192 | final String? text, 193 | final String? subText}) = _$PaletteItemImpl; 194 | 195 | @override 196 | Color get backgroundColor; 197 | @override 198 | Color? get foregroundColor; 199 | @override 200 | String? get text; 201 | @override 202 | String? get subText; 203 | 204 | /// Create a copy of PaletteItem 205 | /// with the given fields replaced by the non-null parameter values. 206 | @override 207 | @JsonKey(includeFromJson: false, includeToJson: false) 208 | _$$PaletteItemImplCopyWith<_$PaletteItemImpl> get copyWith => 209 | throw _privateConstructorUsedError; 210 | } 211 | -------------------------------------------------------------------------------- /lib/feature/color/state/tonal_palette.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 3 | import 'package:freezed_annotation/freezed_annotation.dart'; 4 | import 'package:material_color_utilities/material_color_utilities.dart'; 5 | import 'package:recase/recase.dart'; 6 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 7 | 8 | import '../../../core/ui/component/material.dart'; 9 | import '../../../util/logger.dart'; 10 | import 'current_seed_color.dart'; 11 | import 'palette_item.dart'; 12 | 13 | part 'tonal_palette.freezed.dart'; 14 | part 'tonal_palette.g.dart'; 15 | 16 | @freezed 17 | class TonalPaletteCollection with _$TonalPaletteCollection { 18 | const factory TonalPaletteCollection({ 19 | required TonalPaletteKind kind, 20 | required List items, 21 | }) = _TonalPaletteCollection; 22 | const TonalPaletteCollection._(); 23 | 24 | Color get color => items 25 | .firstWhere((item) => item.shade == TonalPaletteShade.shade600) 26 | .item 27 | .backgroundColor; 28 | 29 | String get title => kind.title; 30 | } 31 | 32 | @freezed 33 | class TonalPaletteItem with _$TonalPaletteItem { 34 | const factory TonalPaletteItem({ 35 | required TonalPaletteKind kind, 36 | required TonalPaletteShade shade, 37 | required PaletteItem item, 38 | }) = _TonalPaletteItem; 39 | } 40 | 41 | enum TonalPaletteKind { 42 | primary, 43 | secondary, 44 | tertiary, 45 | error, 46 | neutral, 47 | neutralVariant, 48 | ; 49 | 50 | String get title => ReCase(name).titleCase; 51 | } 52 | 53 | enum TonalPaletteShade { 54 | shade1, 55 | shade50, 56 | shade100, 57 | shade200, 58 | shade300, 59 | shade400, 60 | shade500, 61 | shade600, 62 | shade700, 63 | shade800, 64 | shade900, 65 | ; 66 | 67 | String get title => name.replaceAll('shade', ''); 68 | } 69 | 70 | @riverpod 71 | List tonalPaletteCollections( 72 | Ref ref, 73 | ) { 74 | final seedColor = ref.watch(currentSeedColorNotifierProvider); 75 | final palette = CorePalette.of(seedColor.value); 76 | return TonalPaletteKind.values 77 | .map( 78 | (kind) => TonalPaletteCollection( 79 | kind: kind, 80 | items: TonalPaletteShade.values 81 | .map( 82 | (shade) => TonalPaletteItem( 83 | kind: kind, 84 | shade: shade, 85 | item: PaletteItem( 86 | backgroundColor: 87 | palette.getMaterialColor(kind).getColor(shade), 88 | text: shade.title, 89 | ), 90 | ), 91 | ) 92 | .toList(), 93 | ), 94 | ) 95 | .toList(); 96 | } 97 | 98 | extension on CorePalette { 99 | MaterialColor getMaterialColor(TonalPaletteKind kind) => switch (kind) { 100 | TonalPaletteKind.primary => primaryMaterial, 101 | TonalPaletteKind.secondary => secondaryMaterial, 102 | TonalPaletteKind.tertiary => tertiaryMaterial, 103 | TonalPaletteKind.error => errorMaterial, 104 | TonalPaletteKind.neutral => neutralMaterial, 105 | TonalPaletteKind.neutralVariant => neutralVariantMaterial, 106 | }; 107 | } 108 | 109 | extension on MaterialColor { 110 | Color getColor(TonalPaletteShade shade) => switch (shade) { 111 | TonalPaletteShade.shade1 => shade1!, 112 | TonalPaletteShade.shade50 => shade50, 113 | TonalPaletteShade.shade100 => shade100, 114 | TonalPaletteShade.shade200 => shade200, 115 | TonalPaletteShade.shade300 => shade300, 116 | TonalPaletteShade.shade400 => shade400, 117 | TonalPaletteShade.shade500 => shade500, 118 | TonalPaletteShade.shade600 => shade600, 119 | TonalPaletteShade.shade700 => shade700, 120 | TonalPaletteShade.shade800 => shade800, 121 | TonalPaletteShade.shade900 => shade900, 122 | }; 123 | } 124 | 125 | @riverpod 126 | String? tonalPaletteTitle( 127 | Ref ref, { 128 | required Color color, 129 | }) { 130 | if (color == Colors.black) { 131 | return 'Black'; 132 | } 133 | if (color == Colors.white) { 134 | return 'White'; 135 | } 136 | final seedColor = ref.watch(currentSeedColorNotifierProvider); 137 | final palette = CorePalette.of(seedColor.value); 138 | final materialColors = { 139 | 'Primary': palette.primaryMaterial, 140 | 'Secondary': palette.secondaryMaterial, 141 | 'Tertiary': palette.tertiaryMaterial, 142 | 'Error': palette.errorMaterial, 143 | 'Neutral': palette.neutralMaterial, 144 | 'NeutralVariant': palette.neutralVariantMaterial, 145 | }; 146 | for (final materialColorKey in materialColors.keys) { 147 | final materialColor = materialColors[materialColorKey]!; 148 | final shades = { 149 | '1': materialColor.shade1, 150 | '50': materialColor.shade50, 151 | '100': materialColor.shade100, 152 | '200': materialColor.shade200, 153 | '300': materialColor.shade300, 154 | '400': materialColor.shade400, 155 | '500': materialColor.shade500, 156 | '600': materialColor.shade600, 157 | '700': materialColor.shade700, 158 | '800': materialColor.shade800, 159 | '900': materialColor.shade900, 160 | }; 161 | for (final shadeKey in shades.keys) { 162 | final shade = shades[shadeKey]!; 163 | if (shade == color) { 164 | return '$materialColorKey$shadeKey'; 165 | } else { 166 | logger.d('$color != $shade($materialColorKey$shadeKey)'); 167 | } 168 | } 169 | } 170 | return null; 171 | } 172 | -------------------------------------------------------------------------------- /lib/feature/color/state/tonal_palette.freezed.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | // GENERATED CODE - DO NOT MODIFY BY HAND 3 | // ignore_for_file: type=lint 4 | // 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 5 | 6 | part of 'tonal_palette.dart'; 7 | 8 | // ************************************************************************** 9 | // FreezedGenerator 10 | // ************************************************************************** 11 | 12 | T _$identity(T value) => value; 13 | 14 | final _privateConstructorUsedError = UnsupportedError( 15 | '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#adding-getters-and-methods-to-our-models'); 16 | 17 | /// @nodoc 18 | mixin _$TonalPaletteCollection { 19 | TonalPaletteKind get kind => throw _privateConstructorUsedError; 20 | List get items => throw _privateConstructorUsedError; 21 | 22 | /// Create a copy of TonalPaletteCollection 23 | /// with the given fields replaced by the non-null parameter values. 24 | @JsonKey(includeFromJson: false, includeToJson: false) 25 | $TonalPaletteCollectionCopyWith get copyWith => 26 | throw _privateConstructorUsedError; 27 | } 28 | 29 | /// @nodoc 30 | abstract class $TonalPaletteCollectionCopyWith<$Res> { 31 | factory $TonalPaletteCollectionCopyWith(TonalPaletteCollection value, 32 | $Res Function(TonalPaletteCollection) then) = 33 | _$TonalPaletteCollectionCopyWithImpl<$Res, TonalPaletteCollection>; 34 | @useResult 35 | $Res call({TonalPaletteKind kind, List items}); 36 | } 37 | 38 | /// @nodoc 39 | class _$TonalPaletteCollectionCopyWithImpl<$Res, 40 | $Val extends TonalPaletteCollection> 41 | implements $TonalPaletteCollectionCopyWith<$Res> { 42 | _$TonalPaletteCollectionCopyWithImpl(this._value, this._then); 43 | 44 | // ignore: unused_field 45 | final $Val _value; 46 | // ignore: unused_field 47 | final $Res Function($Val) _then; 48 | 49 | /// Create a copy of TonalPaletteCollection 50 | /// with the given fields replaced by the non-null parameter values. 51 | @pragma('vm:prefer-inline') 52 | @override 53 | $Res call({ 54 | Object? kind = null, 55 | Object? items = null, 56 | }) { 57 | return _then(_value.copyWith( 58 | kind: null == kind 59 | ? _value.kind 60 | : kind // ignore: cast_nullable_to_non_nullable 61 | as TonalPaletteKind, 62 | items: null == items 63 | ? _value.items 64 | : items // ignore: cast_nullable_to_non_nullable 65 | as List, 66 | ) as $Val); 67 | } 68 | } 69 | 70 | /// @nodoc 71 | abstract class _$$TonalPaletteCollectionImplCopyWith<$Res> 72 | implements $TonalPaletteCollectionCopyWith<$Res> { 73 | factory _$$TonalPaletteCollectionImplCopyWith( 74 | _$TonalPaletteCollectionImpl value, 75 | $Res Function(_$TonalPaletteCollectionImpl) then) = 76 | __$$TonalPaletteCollectionImplCopyWithImpl<$Res>; 77 | @override 78 | @useResult 79 | $Res call({TonalPaletteKind kind, List items}); 80 | } 81 | 82 | /// @nodoc 83 | class __$$TonalPaletteCollectionImplCopyWithImpl<$Res> 84 | extends _$TonalPaletteCollectionCopyWithImpl<$Res, 85 | _$TonalPaletteCollectionImpl> 86 | implements _$$TonalPaletteCollectionImplCopyWith<$Res> { 87 | __$$TonalPaletteCollectionImplCopyWithImpl( 88 | _$TonalPaletteCollectionImpl _value, 89 | $Res Function(_$TonalPaletteCollectionImpl) _then) 90 | : super(_value, _then); 91 | 92 | /// Create a copy of TonalPaletteCollection 93 | /// with the given fields replaced by the non-null parameter values. 94 | @pragma('vm:prefer-inline') 95 | @override 96 | $Res call({ 97 | Object? kind = null, 98 | Object? items = null, 99 | }) { 100 | return _then(_$TonalPaletteCollectionImpl( 101 | kind: null == kind 102 | ? _value.kind 103 | : kind // ignore: cast_nullable_to_non_nullable 104 | as TonalPaletteKind, 105 | items: null == items 106 | ? _value._items 107 | : items // ignore: cast_nullable_to_non_nullable 108 | as List, 109 | )); 110 | } 111 | } 112 | 113 | /// @nodoc 114 | 115 | class _$TonalPaletteCollectionImpl extends _TonalPaletteCollection { 116 | const _$TonalPaletteCollectionImpl( 117 | {required this.kind, required final List items}) 118 | : _items = items, 119 | super._(); 120 | 121 | @override 122 | final TonalPaletteKind kind; 123 | final List _items; 124 | @override 125 | List get items { 126 | if (_items is EqualUnmodifiableListView) return _items; 127 | // ignore: implicit_dynamic_type 128 | return EqualUnmodifiableListView(_items); 129 | } 130 | 131 | @override 132 | String toString() { 133 | return 'TonalPaletteCollection(kind: $kind, items: $items)'; 134 | } 135 | 136 | @override 137 | bool operator ==(Object other) { 138 | return identical(this, other) || 139 | (other.runtimeType == runtimeType && 140 | other is _$TonalPaletteCollectionImpl && 141 | (identical(other.kind, kind) || other.kind == kind) && 142 | const DeepCollectionEquality().equals(other._items, _items)); 143 | } 144 | 145 | @override 146 | int get hashCode => Object.hash( 147 | runtimeType, kind, const DeepCollectionEquality().hash(_items)); 148 | 149 | /// Create a copy of TonalPaletteCollection 150 | /// with the given fields replaced by the non-null parameter values. 151 | @JsonKey(includeFromJson: false, includeToJson: false) 152 | @override 153 | @pragma('vm:prefer-inline') 154 | _$$TonalPaletteCollectionImplCopyWith<_$TonalPaletteCollectionImpl> 155 | get copyWith => __$$TonalPaletteCollectionImplCopyWithImpl< 156 | _$TonalPaletteCollectionImpl>(this, _$identity); 157 | } 158 | 159 | abstract class _TonalPaletteCollection extends TonalPaletteCollection { 160 | const factory _TonalPaletteCollection( 161 | {required final TonalPaletteKind kind, 162 | required final List items}) = 163 | _$TonalPaletteCollectionImpl; 164 | const _TonalPaletteCollection._() : super._(); 165 | 166 | @override 167 | TonalPaletteKind get kind; 168 | @override 169 | List get items; 170 | 171 | /// Create a copy of TonalPaletteCollection 172 | /// with the given fields replaced by the non-null parameter values. 173 | @override 174 | @JsonKey(includeFromJson: false, includeToJson: false) 175 | _$$TonalPaletteCollectionImplCopyWith<_$TonalPaletteCollectionImpl> 176 | get copyWith => throw _privateConstructorUsedError; 177 | } 178 | 179 | /// @nodoc 180 | mixin _$TonalPaletteItem { 181 | TonalPaletteKind get kind => throw _privateConstructorUsedError; 182 | TonalPaletteShade get shade => throw _privateConstructorUsedError; 183 | PaletteItem get item => throw _privateConstructorUsedError; 184 | 185 | /// Create a copy of TonalPaletteItem 186 | /// with the given fields replaced by the non-null parameter values. 187 | @JsonKey(includeFromJson: false, includeToJson: false) 188 | $TonalPaletteItemCopyWith get copyWith => 189 | throw _privateConstructorUsedError; 190 | } 191 | 192 | /// @nodoc 193 | abstract class $TonalPaletteItemCopyWith<$Res> { 194 | factory $TonalPaletteItemCopyWith( 195 | TonalPaletteItem value, $Res Function(TonalPaletteItem) then) = 196 | _$TonalPaletteItemCopyWithImpl<$Res, TonalPaletteItem>; 197 | @useResult 198 | $Res call({TonalPaletteKind kind, TonalPaletteShade shade, PaletteItem item}); 199 | 200 | $PaletteItemCopyWith<$Res> get item; 201 | } 202 | 203 | /// @nodoc 204 | class _$TonalPaletteItemCopyWithImpl<$Res, $Val extends TonalPaletteItem> 205 | implements $TonalPaletteItemCopyWith<$Res> { 206 | _$TonalPaletteItemCopyWithImpl(this._value, this._then); 207 | 208 | // ignore: unused_field 209 | final $Val _value; 210 | // ignore: unused_field 211 | final $Res Function($Val) _then; 212 | 213 | /// Create a copy of TonalPaletteItem 214 | /// with the given fields replaced by the non-null parameter values. 215 | @pragma('vm:prefer-inline') 216 | @override 217 | $Res call({ 218 | Object? kind = null, 219 | Object? shade = null, 220 | Object? item = null, 221 | }) { 222 | return _then(_value.copyWith( 223 | kind: null == kind 224 | ? _value.kind 225 | : kind // ignore: cast_nullable_to_non_nullable 226 | as TonalPaletteKind, 227 | shade: null == shade 228 | ? _value.shade 229 | : shade // ignore: cast_nullable_to_non_nullable 230 | as TonalPaletteShade, 231 | item: null == item 232 | ? _value.item 233 | : item // ignore: cast_nullable_to_non_nullable 234 | as PaletteItem, 235 | ) as $Val); 236 | } 237 | 238 | /// Create a copy of TonalPaletteItem 239 | /// with the given fields replaced by the non-null parameter values. 240 | @override 241 | @pragma('vm:prefer-inline') 242 | $PaletteItemCopyWith<$Res> get item { 243 | return $PaletteItemCopyWith<$Res>(_value.item, (value) { 244 | return _then(_value.copyWith(item: value) as $Val); 245 | }); 246 | } 247 | } 248 | 249 | /// @nodoc 250 | abstract class _$$TonalPaletteItemImplCopyWith<$Res> 251 | implements $TonalPaletteItemCopyWith<$Res> { 252 | factory _$$TonalPaletteItemImplCopyWith(_$TonalPaletteItemImpl value, 253 | $Res Function(_$TonalPaletteItemImpl) then) = 254 | __$$TonalPaletteItemImplCopyWithImpl<$Res>; 255 | @override 256 | @useResult 257 | $Res call({TonalPaletteKind kind, TonalPaletteShade shade, PaletteItem item}); 258 | 259 | @override 260 | $PaletteItemCopyWith<$Res> get item; 261 | } 262 | 263 | /// @nodoc 264 | class __$$TonalPaletteItemImplCopyWithImpl<$Res> 265 | extends _$TonalPaletteItemCopyWithImpl<$Res, _$TonalPaletteItemImpl> 266 | implements _$$TonalPaletteItemImplCopyWith<$Res> { 267 | __$$TonalPaletteItemImplCopyWithImpl(_$TonalPaletteItemImpl _value, 268 | $Res Function(_$TonalPaletteItemImpl) _then) 269 | : super(_value, _then); 270 | 271 | /// Create a copy of TonalPaletteItem 272 | /// with the given fields replaced by the non-null parameter values. 273 | @pragma('vm:prefer-inline') 274 | @override 275 | $Res call({ 276 | Object? kind = null, 277 | Object? shade = null, 278 | Object? item = null, 279 | }) { 280 | return _then(_$TonalPaletteItemImpl( 281 | kind: null == kind 282 | ? _value.kind 283 | : kind // ignore: cast_nullable_to_non_nullable 284 | as TonalPaletteKind, 285 | shade: null == shade 286 | ? _value.shade 287 | : shade // ignore: cast_nullable_to_non_nullable 288 | as TonalPaletteShade, 289 | item: null == item 290 | ? _value.item 291 | : item // ignore: cast_nullable_to_non_nullable 292 | as PaletteItem, 293 | )); 294 | } 295 | } 296 | 297 | /// @nodoc 298 | 299 | class _$TonalPaletteItemImpl implements _TonalPaletteItem { 300 | const _$TonalPaletteItemImpl( 301 | {required this.kind, required this.shade, required this.item}); 302 | 303 | @override 304 | final TonalPaletteKind kind; 305 | @override 306 | final TonalPaletteShade shade; 307 | @override 308 | final PaletteItem item; 309 | 310 | @override 311 | String toString() { 312 | return 'TonalPaletteItem(kind: $kind, shade: $shade, item: $item)'; 313 | } 314 | 315 | @override 316 | bool operator ==(Object other) { 317 | return identical(this, other) || 318 | (other.runtimeType == runtimeType && 319 | other is _$TonalPaletteItemImpl && 320 | (identical(other.kind, kind) || other.kind == kind) && 321 | (identical(other.shade, shade) || other.shade == shade) && 322 | (identical(other.item, item) || other.item == item)); 323 | } 324 | 325 | @override 326 | int get hashCode => Object.hash(runtimeType, kind, shade, item); 327 | 328 | /// Create a copy of TonalPaletteItem 329 | /// with the given fields replaced by the non-null parameter values. 330 | @JsonKey(includeFromJson: false, includeToJson: false) 331 | @override 332 | @pragma('vm:prefer-inline') 333 | _$$TonalPaletteItemImplCopyWith<_$TonalPaletteItemImpl> get copyWith => 334 | __$$TonalPaletteItemImplCopyWithImpl<_$TonalPaletteItemImpl>( 335 | this, _$identity); 336 | } 337 | 338 | abstract class _TonalPaletteItem implements TonalPaletteItem { 339 | const factory _TonalPaletteItem( 340 | {required final TonalPaletteKind kind, 341 | required final TonalPaletteShade shade, 342 | required final PaletteItem item}) = _$TonalPaletteItemImpl; 343 | 344 | @override 345 | TonalPaletteKind get kind; 346 | @override 347 | TonalPaletteShade get shade; 348 | @override 349 | PaletteItem get item; 350 | 351 | /// Create a copy of TonalPaletteItem 352 | /// with the given fields replaced by the non-null parameter values. 353 | @override 354 | @JsonKey(includeFromJson: false, includeToJson: false) 355 | _$$TonalPaletteItemImplCopyWith<_$TonalPaletteItemImpl> get copyWith => 356 | throw _privateConstructorUsedError; 357 | } 358 | -------------------------------------------------------------------------------- /lib/feature/color/state/tonal_palette.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'tonal_palette.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$tonalPaletteCollectionsHash() => 10 | r'e7b69860eb17cd52b2274752080392757dcebd16'; 11 | 12 | /// See also [tonalPaletteCollections]. 13 | @ProviderFor(tonalPaletteCollections) 14 | final tonalPaletteCollectionsProvider = 15 | AutoDisposeProvider>.internal( 16 | tonalPaletteCollections, 17 | name: r'tonalPaletteCollectionsProvider', 18 | debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') 19 | ? null 20 | : _$tonalPaletteCollectionsHash, 21 | dependencies: null, 22 | allTransitiveDependencies: null, 23 | ); 24 | 25 | @Deprecated('Will be removed in 3.0. Use Ref instead') 26 | // ignore: unused_element 27 | typedef TonalPaletteCollectionsRef 28 | = AutoDisposeProviderRef>; 29 | String _$tonalPaletteTitleHash() => r'fa672a5b4edae6081a0cad1f41bdc2bf2e9291c1'; 30 | 31 | /// Copied from Dart SDK 32 | class _SystemHash { 33 | _SystemHash._(); 34 | 35 | static int combine(int hash, int value) { 36 | // ignore: parameter_assignments 37 | hash = 0x1fffffff & (hash + value); 38 | // ignore: parameter_assignments 39 | hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); 40 | return hash ^ (hash >> 6); 41 | } 42 | 43 | static int finish(int hash) { 44 | // ignore: parameter_assignments 45 | hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); 46 | // ignore: parameter_assignments 47 | hash = hash ^ (hash >> 11); 48 | return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); 49 | } 50 | } 51 | 52 | /// See also [tonalPaletteTitle]. 53 | @ProviderFor(tonalPaletteTitle) 54 | const tonalPaletteTitleProvider = TonalPaletteTitleFamily(); 55 | 56 | /// See also [tonalPaletteTitle]. 57 | class TonalPaletteTitleFamily extends Family { 58 | /// See also [tonalPaletteTitle]. 59 | const TonalPaletteTitleFamily(); 60 | 61 | /// See also [tonalPaletteTitle]. 62 | TonalPaletteTitleProvider call({ 63 | required Color color, 64 | }) { 65 | return TonalPaletteTitleProvider( 66 | color: color, 67 | ); 68 | } 69 | 70 | @override 71 | TonalPaletteTitleProvider getProviderOverride( 72 | covariant TonalPaletteTitleProvider provider, 73 | ) { 74 | return call( 75 | color: provider.color, 76 | ); 77 | } 78 | 79 | static const Iterable? _dependencies = null; 80 | 81 | @override 82 | Iterable? get dependencies => _dependencies; 83 | 84 | static const Iterable? _allTransitiveDependencies = null; 85 | 86 | @override 87 | Iterable? get allTransitiveDependencies => 88 | _allTransitiveDependencies; 89 | 90 | @override 91 | String? get name => r'tonalPaletteTitleProvider'; 92 | } 93 | 94 | /// See also [tonalPaletteTitle]. 95 | class TonalPaletteTitleProvider extends AutoDisposeProvider { 96 | /// See also [tonalPaletteTitle]. 97 | TonalPaletteTitleProvider({ 98 | required Color color, 99 | }) : this._internal( 100 | (ref) => tonalPaletteTitle( 101 | ref as TonalPaletteTitleRef, 102 | color: color, 103 | ), 104 | from: tonalPaletteTitleProvider, 105 | name: r'tonalPaletteTitleProvider', 106 | debugGetCreateSourceHash: 107 | const bool.fromEnvironment('dart.vm.product') 108 | ? null 109 | : _$tonalPaletteTitleHash, 110 | dependencies: TonalPaletteTitleFamily._dependencies, 111 | allTransitiveDependencies: 112 | TonalPaletteTitleFamily._allTransitiveDependencies, 113 | color: color, 114 | ); 115 | 116 | TonalPaletteTitleProvider._internal( 117 | super._createNotifier, { 118 | required super.name, 119 | required super.dependencies, 120 | required super.allTransitiveDependencies, 121 | required super.debugGetCreateSourceHash, 122 | required super.from, 123 | required this.color, 124 | }) : super.internal(); 125 | 126 | final Color color; 127 | 128 | @override 129 | Override overrideWith( 130 | String? Function(TonalPaletteTitleRef provider) create, 131 | ) { 132 | return ProviderOverride( 133 | origin: this, 134 | override: TonalPaletteTitleProvider._internal( 135 | (ref) => create(ref as TonalPaletteTitleRef), 136 | from: from, 137 | name: null, 138 | dependencies: null, 139 | allTransitiveDependencies: null, 140 | debugGetCreateSourceHash: null, 141 | color: color, 142 | ), 143 | ); 144 | } 145 | 146 | @override 147 | AutoDisposeProviderElement createElement() { 148 | return _TonalPaletteTitleProviderElement(this); 149 | } 150 | 151 | @override 152 | bool operator ==(Object other) { 153 | return other is TonalPaletteTitleProvider && other.color == color; 154 | } 155 | 156 | @override 157 | int get hashCode { 158 | var hash = _SystemHash.combine(0, runtimeType.hashCode); 159 | hash = _SystemHash.combine(hash, color.hashCode); 160 | 161 | return _SystemHash.finish(hash); 162 | } 163 | } 164 | 165 | @Deprecated('Will be removed in 3.0. Use Ref instead') 166 | // ignore: unused_element 167 | mixin TonalPaletteTitleRef on AutoDisposeProviderRef { 168 | /// The parameter `color` of this provider. 169 | Color get color; 170 | } 171 | 172 | class _TonalPaletteTitleProviderElement 173 | extends AutoDisposeProviderElement with TonalPaletteTitleRef { 174 | _TonalPaletteTitleProviderElement(super.provider); 175 | 176 | @override 177 | Color get color => (origin as TonalPaletteTitleProvider).color; 178 | } 179 | // ignore_for_file: type=lint 180 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package 181 | -------------------------------------------------------------------------------- /lib/feature/color/ui/component/color_scheme.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 3 | 4 | import '../../../../core/ui/component/responsive.dart'; 5 | import '../../state/color_scheme.dart'; 6 | import '../../state/palette_item.dart'; 7 | import 'palette.dart'; 8 | 9 | class ColorSchemes extends ConsumerWidget { 10 | const ColorSchemes({super.key}); 11 | 12 | @override 13 | Widget build(BuildContext context, WidgetRef ref) { 14 | final collection = ref.watch(colorSchemeCollectionProvider); 15 | return Padding( 16 | padding: const EdgeInsets.all(8), 17 | child: Responsive( 18 | mobile: _MobileColorSchemes(collection: collection), 19 | tablet: _TabletColorSchemes(collection: collection), 20 | desktop: _DesktopColorSchemes(collection: collection), 21 | ), 22 | ); 23 | } 24 | } 25 | 26 | class _DesktopColorSchemes extends ConsumerWidget { 27 | const _DesktopColorSchemes({ 28 | required this.collection, 29 | }); 30 | 31 | final ColorSchemeCollection collection; 32 | 33 | @override 34 | Widget build(BuildContext context, WidgetRef ref) { 35 | return Column( 36 | children: [ 37 | Row( 38 | children: [ 39 | Expanded( 40 | child: MainPaletteCollection( 41 | color: collection.getPaletteItem(ColorSchemeKind.primary), 42 | onColor: collection.getPaletteItem(ColorSchemeKind.onPrimary), 43 | ), 44 | ), 45 | Expanded( 46 | child: MainPaletteCollection( 47 | color: collection.getPaletteItem(ColorSchemeKind.secondary), 48 | onColor: collection.getPaletteItem(ColorSchemeKind.onSecondary), 49 | ), 50 | ), 51 | Expanded( 52 | child: MainPaletteCollection( 53 | color: collection.getPaletteItem(ColorSchemeKind.tertiary), 54 | onColor: collection.getPaletteItem(ColorSchemeKind.onTertiary), 55 | ), 56 | ), 57 | Expanded( 58 | child: MainPaletteCollection( 59 | color: collection.getPaletteItem(ColorSchemeKind.error), 60 | onColor: collection.getPaletteItem(ColorSchemeKind.onError), 61 | ), 62 | ), 63 | ], 64 | ), 65 | Row( 66 | children: [ 67 | Expanded( 68 | child: MainPaletteCollection( 69 | color: 70 | collection.getPaletteItem(ColorSchemeKind.primaryContainer), 71 | onColor: collection 72 | .getPaletteItem(ColorSchemeKind.onPrimaryContainer), 73 | ), 74 | ), 75 | Expanded( 76 | child: MainPaletteCollection( 77 | color: collection 78 | .getPaletteItem(ColorSchemeKind.secondaryContainer), 79 | onColor: collection 80 | .getPaletteItem(ColorSchemeKind.onSecondaryContainer), 81 | ), 82 | ), 83 | Expanded( 84 | child: MainPaletteCollection( 85 | color: collection 86 | .getPaletteItem(ColorSchemeKind.tertiaryContainer), 87 | onColor: collection 88 | .getPaletteItem(ColorSchemeKind.onTertiaryContainer), 89 | ), 90 | ), 91 | Expanded( 92 | child: MainPaletteCollection( 93 | color: 94 | collection.getPaletteItem(ColorSchemeKind.errorContainer), 95 | onColor: 96 | collection.getPaletteItem(ColorSchemeKind.onErrorContainer), 97 | ), 98 | ), 99 | ], 100 | ), 101 | Row( 102 | children: [ 103 | Expanded( 104 | child: SubPaletteCollection( 105 | fixed: collection.getPaletteItem(ColorSchemeKind.primaryFixed), 106 | dim: collection.getPaletteItem(ColorSchemeKind.primaryFixedDim), 107 | onFixed: 108 | collection.getPaletteItem(ColorSchemeKind.onPrimaryFixed), 109 | onFixedVariant: collection 110 | .getPaletteItem(ColorSchemeKind.onPrimaryFixedVariant), 111 | ), 112 | ), 113 | Expanded( 114 | child: SubPaletteCollection( 115 | fixed: 116 | collection.getPaletteItem(ColorSchemeKind.secondaryFixed), 117 | dim: collection 118 | .getPaletteItem(ColorSchemeKind.secondaryFixedDim), 119 | onFixed: 120 | collection.getPaletteItem(ColorSchemeKind.onSecondaryFixed), 121 | onFixedVariant: collection 122 | .getPaletteItem(ColorSchemeKind.onSecondaryFixedVariant), 123 | ), 124 | ), 125 | Expanded( 126 | child: SubPaletteCollection( 127 | fixed: collection.getPaletteItem(ColorSchemeKind.tertiaryFixed), 128 | dim: 129 | collection.getPaletteItem(ColorSchemeKind.tertiaryFixedDim), 130 | onFixed: 131 | collection.getPaletteItem(ColorSchemeKind.onTertiaryFixed), 132 | onFixedVariant: collection 133 | .getPaletteItem(ColorSchemeKind.onTertiaryFixedVariant), 134 | ), 135 | ), 136 | Expanded( 137 | child: InversePaletteCollection( 138 | collection: collection, 139 | ), 140 | ), 141 | ], 142 | ), 143 | Padding( 144 | padding: const EdgeInsets.all(4), 145 | child: Row( 146 | children: [ 147 | Expanded( 148 | child: Palette( 149 | item: collection.getPaletteItem( 150 | ColorSchemeKind.surfaceDim, 151 | ), 152 | ), 153 | ), 154 | Expanded( 155 | child: Palette( 156 | item: collection.getPaletteItem( 157 | ColorSchemeKind.surface, 158 | ), 159 | ), 160 | ), 161 | Expanded( 162 | child: Palette( 163 | item: collection.getPaletteItem( 164 | ColorSchemeKind.surfaceBright, 165 | ), 166 | ), 167 | ), 168 | ], 169 | ), 170 | ), 171 | Padding( 172 | padding: const EdgeInsets.all(4), 173 | child: Row( 174 | children: [ 175 | Expanded( 176 | child: Palette( 177 | item: collection.getPaletteItem( 178 | ColorSchemeKind.surfaceContainerLowest, 179 | ), 180 | ), 181 | ), 182 | Expanded( 183 | child: Palette( 184 | item: collection.getPaletteItem( 185 | ColorSchemeKind.surfaceContainerLow, 186 | ), 187 | ), 188 | ), 189 | Expanded( 190 | child: Palette( 191 | item: collection.getPaletteItem( 192 | ColorSchemeKind.surfaceContainer, 193 | ), 194 | ), 195 | ), 196 | Expanded( 197 | child: Palette( 198 | item: collection.getPaletteItem( 199 | ColorSchemeKind.surfaceContainerHigh, 200 | ), 201 | ), 202 | ), 203 | Expanded( 204 | child: Palette( 205 | item: collection.getPaletteItem( 206 | ColorSchemeKind.surfaceContainerHighest, 207 | ), 208 | ), 209 | ), 210 | ], 211 | ), 212 | ), 213 | Padding( 214 | padding: const EdgeInsets.all(4), 215 | child: Row( 216 | children: [ 217 | Expanded( 218 | child: Palette( 219 | item: collection.getPaletteItem( 220 | ColorSchemeKind.onSurface, 221 | ), 222 | ), 223 | ), 224 | Expanded( 225 | child: Palette( 226 | item: collection.getPaletteItem( 227 | ColorSchemeKind.onSurfaceVariant, 228 | ), 229 | ), 230 | ), 231 | Expanded( 232 | child: Palette( 233 | item: collection.getPaletteItem( 234 | ColorSchemeKind.outline, 235 | ), 236 | ), 237 | ), 238 | Expanded( 239 | child: Palette( 240 | item: collection.getPaletteItem( 241 | ColorSchemeKind.outlineVariant, 242 | ), 243 | ), 244 | ), 245 | Expanded( 246 | child: Palette( 247 | item: collection.getPaletteItem( 248 | ColorSchemeKind.scrim, 249 | ), 250 | ), 251 | ), 252 | Expanded( 253 | child: Palette( 254 | item: collection.getPaletteItem( 255 | ColorSchemeKind.shadow, 256 | ), 257 | ), 258 | ), 259 | ], 260 | ), 261 | ), 262 | ], 263 | ); 264 | } 265 | } 266 | 267 | class _TabletColorSchemes extends ConsumerWidget { 268 | const _TabletColorSchemes({ 269 | required this.collection, 270 | }); 271 | 272 | final ColorSchemeCollection collection; 273 | 274 | @override 275 | Widget build(BuildContext context, WidgetRef ref) { 276 | return Column( 277 | children: [ 278 | Row( 279 | children: [ 280 | Expanded( 281 | child: MainPaletteCollection( 282 | color: collection.getPaletteItem(ColorSchemeKind.primary), 283 | onColor: collection.getPaletteItem(ColorSchemeKind.onPrimary), 284 | ), 285 | ), 286 | Expanded( 287 | child: MainPaletteCollection( 288 | color: collection.getPaletteItem(ColorSchemeKind.secondary), 289 | onColor: collection.getPaletteItem(ColorSchemeKind.onSecondary), 290 | ), 291 | ), 292 | ], 293 | ), 294 | Row( 295 | children: [ 296 | Expanded( 297 | child: MainPaletteCollection( 298 | color: collection.getPaletteItem(ColorSchemeKind.tertiary), 299 | onColor: collection.getPaletteItem(ColorSchemeKind.onTertiary), 300 | ), 301 | ), 302 | Expanded( 303 | child: MainPaletteCollection( 304 | color: collection.getPaletteItem(ColorSchemeKind.error), 305 | onColor: collection.getPaletteItem(ColorSchemeKind.onError), 306 | ), 307 | ), 308 | ], 309 | ), 310 | Row( 311 | children: [ 312 | Expanded( 313 | child: MainPaletteCollection( 314 | color: 315 | collection.getPaletteItem(ColorSchemeKind.primaryContainer), 316 | onColor: collection 317 | .getPaletteItem(ColorSchemeKind.onPrimaryContainer), 318 | ), 319 | ), 320 | Expanded( 321 | child: MainPaletteCollection( 322 | color: collection 323 | .getPaletteItem(ColorSchemeKind.secondaryContainer), 324 | onColor: collection 325 | .getPaletteItem(ColorSchemeKind.onSecondaryContainer), 326 | ), 327 | ), 328 | ], 329 | ), 330 | Row( 331 | children: [ 332 | Expanded( 333 | child: MainPaletteCollection( 334 | color: collection 335 | .getPaletteItem(ColorSchemeKind.tertiaryContainer), 336 | onColor: collection 337 | .getPaletteItem(ColorSchemeKind.onTertiaryContainer), 338 | ), 339 | ), 340 | Expanded( 341 | child: MainPaletteCollection( 342 | color: 343 | collection.getPaletteItem(ColorSchemeKind.errorContainer), 344 | onColor: 345 | collection.getPaletteItem(ColorSchemeKind.onErrorContainer), 346 | ), 347 | ), 348 | ], 349 | ), 350 | Row( 351 | children: [ 352 | Expanded( 353 | child: SubPaletteCollection( 354 | fixed: collection.getPaletteItem(ColorSchemeKind.primaryFixed), 355 | dim: collection.getPaletteItem(ColorSchemeKind.primaryFixedDim), 356 | onFixed: 357 | collection.getPaletteItem(ColorSchemeKind.onPrimaryFixed), 358 | onFixedVariant: collection 359 | .getPaletteItem(ColorSchemeKind.onPrimaryFixedVariant), 360 | ), 361 | ), 362 | Expanded( 363 | child: SubPaletteCollection( 364 | fixed: 365 | collection.getPaletteItem(ColorSchemeKind.secondaryFixed), 366 | dim: collection 367 | .getPaletteItem(ColorSchemeKind.secondaryFixedDim), 368 | onFixed: 369 | collection.getPaletteItem(ColorSchemeKind.onSecondaryFixed), 370 | onFixedVariant: collection 371 | .getPaletteItem(ColorSchemeKind.onSecondaryFixedVariant), 372 | ), 373 | ), 374 | ], 375 | ), 376 | Row( 377 | children: [ 378 | Expanded( 379 | child: SubPaletteCollection( 380 | fixed: collection.getPaletteItem(ColorSchemeKind.tertiaryFixed), 381 | dim: 382 | collection.getPaletteItem(ColorSchemeKind.tertiaryFixedDim), 383 | onFixed: 384 | collection.getPaletteItem(ColorSchemeKind.onTertiaryFixed), 385 | onFixedVariant: collection 386 | .getPaletteItem(ColorSchemeKind.onTertiaryFixedVariant), 387 | ), 388 | ), 389 | Expanded( 390 | child: InversePaletteCollection( 391 | collection: collection, 392 | ), 393 | ), 394 | ], 395 | ), 396 | Padding( 397 | padding: const EdgeInsets.all(4), 398 | child: Column( 399 | children: [ 400 | Palette( 401 | item: collection.getPaletteItem( 402 | ColorSchemeKind.surfaceDim, 403 | ), 404 | ), 405 | Palette( 406 | item: collection.getPaletteItem( 407 | ColorSchemeKind.surface, 408 | ), 409 | ), 410 | Palette( 411 | item: collection.getPaletteItem( 412 | ColorSchemeKind.surfaceBright, 413 | ), 414 | ), 415 | ], 416 | ), 417 | ), 418 | Padding( 419 | padding: const EdgeInsets.all(4), 420 | child: Column( 421 | children: [ 422 | Palette( 423 | item: collection.getPaletteItem( 424 | ColorSchemeKind.surfaceContainerLowest, 425 | ), 426 | ), 427 | Palette( 428 | item: collection.getPaletteItem( 429 | ColorSchemeKind.surfaceContainerLow, 430 | ), 431 | ), 432 | Palette( 433 | item: collection.getPaletteItem( 434 | ColorSchemeKind.surfaceContainer, 435 | ), 436 | ), 437 | Palette( 438 | item: collection.getPaletteItem( 439 | ColorSchemeKind.surfaceContainerHigh, 440 | ), 441 | ), 442 | Palette( 443 | item: collection.getPaletteItem( 444 | ColorSchemeKind.surfaceContainerHighest, 445 | ), 446 | ), 447 | ], 448 | ), 449 | ), 450 | Padding( 451 | padding: const EdgeInsets.all(4), 452 | child: Column( 453 | children: [ 454 | Palette( 455 | item: collection.getPaletteItem( 456 | ColorSchemeKind.onSurface, 457 | ), 458 | ), 459 | Palette( 460 | item: collection.getPaletteItem( 461 | ColorSchemeKind.onSurfaceVariant, 462 | ), 463 | ), 464 | Palette( 465 | item: collection.getPaletteItem( 466 | ColorSchemeKind.outline, 467 | ), 468 | ), 469 | Palette( 470 | item: collection.getPaletteItem( 471 | ColorSchemeKind.outlineVariant, 472 | ), 473 | ), 474 | Palette( 475 | item: collection.getPaletteItem( 476 | ColorSchemeKind.scrim, 477 | ), 478 | ), 479 | Palette( 480 | item: collection.getPaletteItem( 481 | ColorSchemeKind.shadow, 482 | ), 483 | ), 484 | ], 485 | ), 486 | ), 487 | ], 488 | ); 489 | } 490 | } 491 | 492 | class _MobileColorSchemes extends ConsumerWidget { 493 | const _MobileColorSchemes({ 494 | required this.collection, 495 | }); 496 | 497 | final ColorSchemeCollection collection; 498 | 499 | @override 500 | Widget build(BuildContext context, WidgetRef ref) { 501 | return Column( 502 | children: [ 503 | MainPaletteCollection( 504 | color: collection.getPaletteItem(ColorSchemeKind.primary), 505 | onColor: collection.getPaletteItem(ColorSchemeKind.onPrimary), 506 | ), 507 | MainPaletteCollection( 508 | color: collection.getPaletteItem(ColorSchemeKind.secondary), 509 | onColor: collection.getPaletteItem(ColorSchemeKind.onSecondary), 510 | ), 511 | MainPaletteCollection( 512 | color: collection.getPaletteItem(ColorSchemeKind.tertiary), 513 | onColor: collection.getPaletteItem(ColorSchemeKind.onTertiary), 514 | ), 515 | MainPaletteCollection( 516 | color: collection.getPaletteItem(ColorSchemeKind.error), 517 | onColor: collection.getPaletteItem(ColorSchemeKind.onError), 518 | ), 519 | MainPaletteCollection( 520 | color: collection.getPaletteItem(ColorSchemeKind.primaryContainer), 521 | onColor: 522 | collection.getPaletteItem(ColorSchemeKind.onPrimaryContainer), 523 | ), 524 | MainPaletteCollection( 525 | color: collection.getPaletteItem(ColorSchemeKind.secondaryContainer), 526 | onColor: 527 | collection.getPaletteItem(ColorSchemeKind.onSecondaryContainer), 528 | ), 529 | MainPaletteCollection( 530 | color: collection.getPaletteItem(ColorSchemeKind.tertiaryContainer), 531 | onColor: 532 | collection.getPaletteItem(ColorSchemeKind.onTertiaryContainer), 533 | ), 534 | MainPaletteCollection( 535 | color: collection.getPaletteItem(ColorSchemeKind.errorContainer), 536 | onColor: collection.getPaletteItem(ColorSchemeKind.onErrorContainer), 537 | ), 538 | SubPaletteCollection( 539 | fixed: collection.getPaletteItem(ColorSchemeKind.primaryFixed), 540 | dim: collection.getPaletteItem(ColorSchemeKind.primaryFixedDim), 541 | onFixed: collection.getPaletteItem(ColorSchemeKind.onPrimaryFixed), 542 | onFixedVariant: 543 | collection.getPaletteItem(ColorSchemeKind.onPrimaryFixedVariant), 544 | ), 545 | SubPaletteCollection( 546 | fixed: collection.getPaletteItem(ColorSchemeKind.secondaryFixed), 547 | dim: collection.getPaletteItem(ColorSchemeKind.secondaryFixedDim), 548 | onFixed: collection.getPaletteItem(ColorSchemeKind.onSecondaryFixed), 549 | onFixedVariant: collection 550 | .getPaletteItem(ColorSchemeKind.onSecondaryFixedVariant), 551 | ), 552 | SubPaletteCollection( 553 | fixed: collection.getPaletteItem(ColorSchemeKind.tertiaryFixed), 554 | dim: collection.getPaletteItem(ColorSchemeKind.tertiaryFixedDim), 555 | onFixed: collection.getPaletteItem(ColorSchemeKind.onTertiaryFixed), 556 | onFixedVariant: 557 | collection.getPaletteItem(ColorSchemeKind.onTertiaryFixedVariant), 558 | ), 559 | InversePaletteCollection( 560 | collection: collection, 561 | ), 562 | Padding( 563 | padding: const EdgeInsets.all(4), 564 | child: Column( 565 | children: [ 566 | Palette( 567 | item: collection.getPaletteItem( 568 | ColorSchemeKind.surfaceDim, 569 | ), 570 | ), 571 | Palette( 572 | item: collection.getPaletteItem( 573 | ColorSchemeKind.surface, 574 | ), 575 | ), 576 | Palette( 577 | item: collection.getPaletteItem( 578 | ColorSchemeKind.surfaceBright, 579 | ), 580 | ), 581 | ], 582 | ), 583 | ), 584 | Padding( 585 | padding: const EdgeInsets.all(4), 586 | child: Column( 587 | children: [ 588 | Palette( 589 | item: collection.getPaletteItem( 590 | ColorSchemeKind.surfaceContainerLowest, 591 | ), 592 | ), 593 | Palette( 594 | item: collection.getPaletteItem( 595 | ColorSchemeKind.surfaceContainerLow, 596 | ), 597 | ), 598 | Palette( 599 | item: collection.getPaletteItem( 600 | ColorSchemeKind.surfaceContainer, 601 | ), 602 | ), 603 | Palette( 604 | item: collection.getPaletteItem( 605 | ColorSchemeKind.surfaceContainerHigh, 606 | ), 607 | ), 608 | Palette( 609 | item: collection.getPaletteItem( 610 | ColorSchemeKind.surfaceContainerHighest, 611 | ), 612 | ), 613 | ], 614 | ), 615 | ), 616 | Padding( 617 | padding: const EdgeInsets.all(4), 618 | child: Column( 619 | children: [ 620 | Palette( 621 | item: collection.getPaletteItem( 622 | ColorSchemeKind.onSurface, 623 | ), 624 | ), 625 | Palette( 626 | item: collection.getPaletteItem( 627 | ColorSchemeKind.onSurfaceVariant, 628 | ), 629 | ), 630 | Palette( 631 | item: collection.getPaletteItem( 632 | ColorSchemeKind.outline, 633 | ), 634 | ), 635 | Palette( 636 | item: collection.getPaletteItem( 637 | ColorSchemeKind.outlineVariant, 638 | ), 639 | ), 640 | Palette( 641 | item: collection.getPaletteItem( 642 | ColorSchemeKind.scrim, 643 | ), 644 | ), 645 | Palette( 646 | item: collection.getPaletteItem( 647 | ColorSchemeKind.shadow, 648 | ), 649 | ), 650 | ], 651 | ), 652 | ), 653 | ], 654 | ); 655 | } 656 | } 657 | 658 | class MainPaletteCollection extends ConsumerWidget { 659 | const MainPaletteCollection({ 660 | super.key, 661 | required this.color, 662 | required this.onColor, 663 | }); 664 | 665 | final PaletteItem color; 666 | final PaletteItem onColor; 667 | 668 | @override 669 | Widget build(BuildContext context, WidgetRef ref) { 670 | return Padding( 671 | padding: const EdgeInsets.all(4), 672 | child: Column( 673 | children: [ 674 | Palette(item: color), 675 | Palette(item: onColor), 676 | ], 677 | ), 678 | ); 679 | } 680 | } 681 | 682 | class InversePaletteCollection extends ConsumerWidget { 683 | const InversePaletteCollection({ 684 | super.key, 685 | required this.collection, 686 | }); 687 | 688 | final ColorSchemeCollection collection; 689 | 690 | @override 691 | Widget build(BuildContext context, WidgetRef ref) { 692 | return Padding( 693 | padding: const EdgeInsets.all(4), 694 | child: Column( 695 | children: [ 696 | Palette( 697 | item: collection.getPaletteItem(ColorSchemeKind.inverseSurface), 698 | ), 699 | Palette( 700 | item: collection.getPaletteItem(ColorSchemeKind.onInverseSurface), 701 | ), 702 | Palette( 703 | item: collection.getPaletteItem(ColorSchemeKind.inversePrimary), 704 | ), 705 | ], 706 | ), 707 | ); 708 | } 709 | } 710 | 711 | class SubPaletteCollection extends ConsumerWidget { 712 | const SubPaletteCollection({ 713 | super.key, 714 | required this.fixed, 715 | required this.dim, 716 | required this.onFixed, 717 | required this.onFixedVariant, 718 | }); 719 | 720 | final PaletteItem fixed; 721 | final PaletteItem dim; 722 | final PaletteItem onFixed; 723 | final PaletteItem onFixedVariant; 724 | 725 | @override 726 | Widget build(BuildContext context, WidgetRef ref) { 727 | return Padding( 728 | padding: const EdgeInsets.all(4), 729 | child: Column( 730 | children: [ 731 | Row( 732 | children: [ 733 | Expanded( 734 | child: Palette(item: fixed), 735 | ), 736 | Expanded( 737 | child: Palette(item: dim), 738 | ), 739 | ], 740 | ), 741 | Palette(item: onFixed), 742 | Palette(item: onFixedVariant), 743 | ], 744 | ), 745 | ); 746 | } 747 | } 748 | -------------------------------------------------------------------------------- /lib/feature/color/ui/component/palette.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 4 | import 'package:responsive_framework/responsive_framework.dart'; 5 | 6 | import '../../../../../core/ui/component/layout.dart'; 7 | import '../../../../../core/ui/component/material.dart'; 8 | import '../../../../core/ui/component/widget_ref_x.dart'; 9 | import '../../../theme_mode/state/current_theme_mode.dart'; 10 | import '../../state/current_hover_color.dart'; 11 | import '../../state/palette_item.dart'; 12 | 13 | class Palette extends ConsumerStatefulWidget { 14 | const Palette({ 15 | super.key, 16 | required this.item, 17 | this.padding = const EdgeInsets.all(p8), 18 | }); 19 | 20 | final PaletteItem item; 21 | final EdgeInsets padding; 22 | 23 | @override 24 | ConsumerState createState() => _PaletteState(); 25 | } 26 | 27 | class _PaletteState extends ConsumerState { 28 | bool isHover = false; 29 | 30 | Color get textColor => 31 | widget.item.foregroundColor ?? 32 | (widget.item.backgroundColor.computeLuminance() < 0.5 33 | ? Colors.white 34 | : Colors.black); 35 | 36 | Color get lightBorderColor => 37 | widget.item.backgroundColor.computeLuminance() < 0.5 38 | ? context.primaryContainer 39 | : context.primary; 40 | 41 | Color get darkBorderColor => 42 | widget.item.backgroundColor.computeLuminance() < 0.5 43 | ? context.primary 44 | : context.primaryContainer; 45 | 46 | TextStyle get textStyle => TextStyle( 47 | color: textColor, 48 | fontSize: ResponsiveValue( 49 | context, 50 | defaultValue: 10, 51 | valueWhen: [ 52 | const Condition.largerThan( 53 | name: MOBILE, 54 | value: 12, 55 | ), 56 | ], 57 | ).value, 58 | ); 59 | 60 | Widget get foreground { 61 | final text = widget.item.text; 62 | if (text == null) { 63 | return const SizedBox(); 64 | } 65 | 66 | final subText = widget.item.subText; 67 | if (subText == null) { 68 | return Center( 69 | child: Text( 70 | text, 71 | style: textStyle, 72 | ), 73 | ); 74 | } 75 | 76 | return Column( 77 | children: [ 78 | Expanded( 79 | child: Align( 80 | alignment: Alignment.topLeft, 81 | child: Text( 82 | text, 83 | style: textStyle, 84 | ), 85 | ), 86 | ), 87 | Align( 88 | alignment: Alignment.bottomRight, 89 | child: Text( 90 | subText, 91 | style: textStyle, 92 | ), 93 | ), 94 | ], 95 | ); 96 | } 97 | 98 | @override 99 | Widget build(BuildContext context) { 100 | ref.listen(currentHoverColorProvider, (previous, next) { 101 | if (widget.item.backgroundColor != next) { 102 | setState(() { 103 | isHover = false; 104 | }); 105 | } 106 | }); 107 | 108 | if (widget.item.backgroundColor == Colors.transparent) { 109 | return const SizedBox( 110 | height: paletteHeight, 111 | ); 112 | } 113 | 114 | final currentThemeMode = ref.watch(currentThemeModeProvider); 115 | final isLight = currentThemeMode == ThemeMode.light; 116 | final currentHoverColor = ref.watch(currentHoverColorProvider); 117 | return InkWell( 118 | onHover: (value) { 119 | setState(() { 120 | isHover = value; 121 | 122 | final notifier = ref.read(currentHoverColorProvider.notifier); 123 | if (isHover) { 124 | notifier.update(widget.item.backgroundColor); 125 | } else { 126 | notifier.clear(); 127 | } 128 | }); 129 | }, 130 | onTap: () async { 131 | // クリップボードにHEX文字列をコピー 132 | final hex = widget.item.backgroundColor.toHexString(); 133 | await Clipboard.setData(ClipboardData(text: hex)); 134 | 135 | // スナックバーを表示する 136 | ref.showSnackBar( 137 | SnackBar( 138 | content: Text('$hex Copied!'), 139 | width: snackBarWidth, 140 | ), 141 | ); 142 | }, 143 | child: DecoratedBox( 144 | decoration: BoxDecoration( 145 | color: widget.item.backgroundColor, 146 | border: currentHoverColor == widget.item.backgroundColor 147 | ? Border.all( 148 | color: isLight ? lightBorderColor : darkBorderColor, 149 | width: 3, 150 | ) 151 | : null, 152 | ), 153 | child: SizedBox( 154 | height: paletteHeight, 155 | child: Padding( 156 | padding: widget.padding, 157 | child: foreground, 158 | ), 159 | ), 160 | ), 161 | ); 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /lib/feature/color/ui/component/tonal_palette.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 4 | import 'package:gap/gap.dart'; 5 | 6 | import '../../../../../core/ui/component/layout.dart'; 7 | import '../../../../../core/ui/component/material.dart'; 8 | import '../../../../../core/ui/component/responsive.dart'; 9 | import '../../../../core/ui/component/widget_ref_x.dart'; 10 | import '../../state/tonal_palette.dart'; 11 | import 'palette.dart'; 12 | 13 | class TonalPalettes extends ConsumerWidget { 14 | const TonalPalettes({ 15 | super.key, 16 | this.padding = const EdgeInsets.all(p8), 17 | }); 18 | 19 | final EdgeInsets padding; 20 | 21 | @override 22 | Widget build(BuildContext context, WidgetRef ref) { 23 | final collections = ref.watch(tonalPaletteCollectionsProvider); 24 | return Padding( 25 | padding: padding, 26 | child: Column( 27 | children: [ 28 | ...collections 29 | .map((collection) => _PaletteRow(collection: collection)), 30 | ], 31 | ), 32 | ); 33 | } 34 | } 35 | 36 | class _PaletteRow extends StatelessWidget { 37 | const _PaletteRow({ 38 | required this.collection, 39 | }); 40 | 41 | final TonalPaletteCollection collection; 42 | 43 | @override 44 | Widget build(BuildContext context) { 45 | return Responsive( 46 | mobile: _Mobile( 47 | collection: collection, 48 | ), 49 | tablet: _Tablet( 50 | collection: collection, 51 | ), 52 | desktop: _Desktop( 53 | collection: collection, 54 | ), 55 | ); 56 | } 57 | } 58 | 59 | class _Mobile extends ConsumerWidget { 60 | const _Mobile({ 61 | required this.collection, 62 | }); 63 | 64 | final TonalPaletteCollection collection; 65 | 66 | @override 67 | Widget build(BuildContext context, WidgetRef ref) { 68 | return Padding( 69 | padding: const EdgeInsets.all(4), 70 | child: Column( 71 | crossAxisAlignment: CrossAxisAlignment.start, 72 | children: [ 73 | Row( 74 | children: [ 75 | _PrimaryCircleColor( 76 | color: collection.color, 77 | ), 78 | const Gap(24), 79 | _TitleText(title: collection.title), 80 | ], 81 | ), 82 | const Gap(p8), 83 | Column( 84 | children: [ 85 | ...collection.items.map( 86 | (item) => SizedBox( 87 | height: 40, 88 | child: Palette(item: item.item), 89 | ), 90 | ), 91 | ], 92 | ), 93 | const SizedBox(height: p8), 94 | ], 95 | ), 96 | ); 97 | } 98 | } 99 | 100 | class _Tablet extends ConsumerWidget { 101 | const _Tablet({ 102 | required this.collection, 103 | }); 104 | 105 | final TonalPaletteCollection collection; 106 | 107 | @override 108 | Widget build(BuildContext context, WidgetRef ref) { 109 | return Padding( 110 | padding: const EdgeInsets.all(4), 111 | child: Column( 112 | crossAxisAlignment: CrossAxisAlignment.start, 113 | children: [ 114 | Row( 115 | children: [ 116 | _PrimaryCircleColor( 117 | color: collection.color, 118 | ), 119 | const Gap(24), 120 | _TitleText(title: collection.title), 121 | ], 122 | ), 123 | const Gap(p8), 124 | Row( 125 | children: [ 126 | ...collection.items.map( 127 | (item) => Expanded( 128 | child: Palette(item: item.item), 129 | ), 130 | ), 131 | ], 132 | ), 133 | const SizedBox(height: p8), 134 | ], 135 | ), 136 | ); 137 | } 138 | } 139 | 140 | class _Desktop extends ConsumerWidget { 141 | const _Desktop({ 142 | required this.collection, 143 | }); 144 | 145 | final TonalPaletteCollection collection; 146 | 147 | @override 148 | Widget build(BuildContext context, WidgetRef ref) { 149 | return Padding( 150 | padding: const EdgeInsets.all(4), 151 | child: Row( 152 | children: [ 153 | _PrimaryCircleColor( 154 | color: collection.color, 155 | ), 156 | const Gap(24), 157 | _TitleText(title: collection.title), 158 | const Gap(24), 159 | ...collection.items.map( 160 | (item) => Expanded( 161 | child: Palette(item: item.item), 162 | ), 163 | ), 164 | ], 165 | ), 166 | ); 167 | } 168 | } 169 | 170 | class _TitleText extends StatelessWidget { 171 | const _TitleText({ 172 | required this.title, 173 | }); 174 | 175 | final String title; 176 | 177 | @override 178 | Widget build(BuildContext context) { 179 | return SizedBox( 180 | width: 120, 181 | child: Text( 182 | title, 183 | style: context.titleMedium, 184 | maxLines: 1, 185 | overflow: TextOverflow.ellipsis, 186 | ), 187 | ); 188 | } 189 | } 190 | 191 | class _PrimaryCircleColor extends ConsumerWidget { 192 | const _PrimaryCircleColor({ 193 | required this.color, 194 | }); 195 | 196 | final Color color; 197 | static const colorDimention = 42.0; 198 | 199 | @override 200 | Widget build(BuildContext context, WidgetRef ref) { 201 | return SizedBox.square( 202 | dimension: colorDimention, 203 | child: InkWell( 204 | onTap: () async { 205 | // クリップボードにHEX文字列をコピー 206 | final hex = color.toHexString(); 207 | await Clipboard.setData(ClipboardData(text: hex)); 208 | 209 | // スナックバーを表示する 210 | ref.showSnackBar( 211 | SnackBar( 212 | content: Text('$hex Copied!'), 213 | width: snackBarWidth, 214 | ), 215 | ); 216 | }, 217 | child: CircleAvatar( 218 | backgroundColor: color, 219 | ), 220 | ), 221 | ); 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /lib/feature/home/ui/page/component/copy_right.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import '../../../../../core/ui/component/material.dart'; 4 | 5 | class CopyRightText extends StatefulWidget { 6 | const CopyRightText({super.key}); 7 | 8 | @override 9 | State createState() => _CopyRightTextState(); 10 | } 11 | 12 | class _CopyRightTextState extends State { 13 | Future _getFlutterVersion() async { 14 | try { 15 | final flutterVersion = await DefaultAssetBundle.of(context) 16 | .loadString('.dart_tool/version'); 17 | return flutterVersion; 18 | } catch (_) { 19 | // The asset might not be available in packaged builds. 20 | return null; 21 | } 22 | } 23 | 24 | @override 25 | Widget build(BuildContext context) { 26 | return FutureBuilder( 27 | future: _getFlutterVersion(), 28 | builder: (context, snapshot) { 29 | final flutterVersion = snapshot.data; 30 | return Padding( 31 | padding: const EdgeInsets.symmetric(vertical: 8), 32 | child: DefaultTextStyle.merge( 33 | style: TextStyle( 34 | color: context.outline, 35 | fontSize: 12, 36 | ), 37 | child: Row( 38 | mainAxisAlignment: MainAxisAlignment.center, 39 | children: [ 40 | Text( 41 | 'powered by Flutter${flutterVersion != null ? ' $flutterVersion' : ''} ', 42 | ), 43 | const Text('(C) 2023 susatthi.'), 44 | ], 45 | ), 46 | ), 47 | ); 48 | }, 49 | ); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/feature/home/ui/page/component/home_drawer.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 3 | import 'package:gap/gap.dart'; 4 | 5 | import '../../../../../core/ui/component/layout.dart'; 6 | import '../../../../../core/ui/component/material.dart'; 7 | import '../../../../app/ui/component/app_version.dart'; 8 | import '../../../../seed_color_history/ui/component/dynamic_scheme_variant.dart'; 9 | import 'copy_right.dart'; 10 | import 'launch_github_button.dart'; 11 | import 'seed_color.dart'; 12 | 13 | const homeDrawerWidth = 360.0; 14 | 15 | class HomeDrawer extends ConsumerWidget { 16 | const HomeDrawer({super.key}); 17 | 18 | @override 19 | Widget build(BuildContext context, WidgetRef ref) { 20 | return const Drawer( 21 | width: homeDrawerWidth, 22 | child: Column( 23 | crossAxisAlignment: CrossAxisAlignment.stretch, 24 | children: [ 25 | _Content(), 26 | ], 27 | ), 28 | ); 29 | } 30 | } 31 | 32 | class HomeDrawerContent extends StatelessWidget { 33 | const HomeDrawerContent({super.key}); 34 | 35 | @override 36 | Widget build(BuildContext context) { 37 | return ColoredBox( 38 | color: DrawerTheme.of(context).backgroundColor ?? 39 | context.surfaceContainerLow, 40 | child: const SizedBox( 41 | width: homeDrawerWidth, 42 | child: Column( 43 | crossAxisAlignment: CrossAxisAlignment.stretch, 44 | children: [ 45 | _Content(), 46 | ], 47 | ), 48 | ), 49 | ); 50 | } 51 | } 52 | 53 | class _Content extends StatelessWidget { 54 | const _Content(); 55 | 56 | @override 57 | Widget build(BuildContext context) { 58 | return Expanded( 59 | child: LayoutBuilder( 60 | builder: (context, constraints) { 61 | return SingleChildScrollView( 62 | child: ConstrainedBox( 63 | constraints: BoxConstraints(minHeight: constraints.maxHeight), 64 | child: const IntrinsicHeight( 65 | child: Column( 66 | children: [ 67 | SeedColorPicker(), 68 | Divider( 69 | indent: p8, 70 | endIndent: p8, 71 | ), 72 | DynamicSchemeVariantChips(), 73 | Spacer(), 74 | _Footer(), 75 | ], 76 | ), 77 | ), 78 | ), 79 | ); 80 | }, 81 | ), 82 | ); 83 | } 84 | } 85 | 86 | class _Footer extends StatelessWidget { 87 | const _Footer(); 88 | 89 | @override 90 | Widget build(BuildContext context) { 91 | return const Column( 92 | crossAxisAlignment: CrossAxisAlignment.end, 93 | children: [ 94 | LaunchGitHubButton(), 95 | Row( 96 | mainAxisSize: MainAxisSize.min, 97 | children: [ 98 | AppVersionText(), 99 | Gap(p8), 100 | CopyRightText(), 101 | ], 102 | ), 103 | ], 104 | ); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /lib/feature/home/ui/page/component/home_panel.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import '../../../../../../core/ui/component/layout.dart'; 4 | import '../../../../../../core/ui/component/material.dart'; 5 | 6 | class HomePanel extends StatelessWidget { 7 | const HomePanel({ 8 | super.key, 9 | this.title, 10 | required this.child, 11 | }); 12 | 13 | final String? title; 14 | final Widget child; 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return Column( 19 | crossAxisAlignment: CrossAxisAlignment.start, 20 | children: [ 21 | const SizedBox(height: p8), 22 | if (title != null) 23 | Padding( 24 | padding: const EdgeInsets.all(p8), 25 | child: _TitleText(title: title!), 26 | ), 27 | child, 28 | ], 29 | ); 30 | } 31 | } 32 | 33 | class _TitleText extends StatelessWidget { 34 | const _TitleText({ 35 | required this.title, 36 | }); 37 | 38 | final String title; 39 | 40 | @override 41 | Widget build(BuildContext context) { 42 | return Text( 43 | title, 44 | style: context.titleLarge, 45 | ); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/feature/home/ui/page/component/home_title.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:responsive_framework/responsive_framework.dart'; 3 | 4 | import '../../../../../util/assets/assets.gen.dart'; 5 | 6 | class HomeTitle extends StatelessWidget { 7 | const HomeTitle({super.key}); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | final isMobile = ResponsiveWrapper.of(context).isSmallerThan(MOBILE); 12 | return Row( 13 | children: [ 14 | if (!isMobile) const Logo(), 15 | const Text( 16 | 'Material Color System', 17 | style: TextStyle(fontSize: 20), 18 | ), 19 | ], 20 | ); 21 | } 22 | } 23 | 24 | class Logo extends StatelessWidget { 25 | const Logo({ 26 | super.key, 27 | this.size = 32, 28 | this.padding = const EdgeInsets.all(8), 29 | }); 30 | 31 | final double size; 32 | final EdgeInsets padding; 33 | 34 | @override 35 | Widget build(BuildContext context) { 36 | return Padding( 37 | padding: padding, 38 | child: Assets.images.appIcon.image( 39 | width: size, 40 | height: size, 41 | ), 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/feature/home/ui/page/component/launch_github_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 3 | import 'package:gap/gap.dart'; 4 | 5 | import '../../../../../core/ui/component/layout.dart'; 6 | import '../../../../launcher/use_case/launch_url.dart'; 7 | 8 | class LaunchGitHubButton extends ConsumerWidget { 9 | const LaunchGitHubButton({super.key}); 10 | 11 | @override 12 | Widget build(BuildContext context, WidgetRef ref) { 13 | return OutlinedButton( 14 | onPressed: () => ref.read(launchUrlUseCaseProvider.notifier).invoke( 15 | Uri.parse( 16 | 'https://github.com/susatthi/flutter-material-color-system', 17 | ), 18 | ), 19 | child: const Row( 20 | mainAxisSize: MainAxisSize.min, 21 | children: [ 22 | Text('GitHub'), 23 | Gap(p8), 24 | Icon(Icons.launch, size: 18), 25 | ], 26 | ), 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/feature/home/ui/page/component/seed_color.dart: -------------------------------------------------------------------------------- 1 | import 'package:flex_color_picker/flex_color_picker.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 4 | import 'package:gap/gap.dart'; 5 | 6 | import '../../../../../core/ui/component/layout.dart'; 7 | import '../../../../../core/ui/component/material.dart'; 8 | import '../../../../color/state/current_seed_color.dart'; 9 | import '../../../../seed_color_history/ui/component/seed_color_history.dart'; 10 | 11 | class ShowSeedColorPickerDialogButton extends ConsumerWidget { 12 | const ShowSeedColorPickerDialogButton({super.key}); 13 | 14 | @override 15 | Widget build(BuildContext context, WidgetRef ref) { 16 | final seedColor = ref.watch(currentSeedColorNotifierProvider); 17 | return IconButton( 18 | onPressed: () => showDialog( 19 | context: context, 20 | builder: (context) => const SeedColorPickerDialog(), 21 | ), 22 | icon: SizedBox.square( 23 | dimension: 24, 24 | child: CircleAvatar( 25 | backgroundColor: seedColor, 26 | ), 27 | ), 28 | tooltip: 'Change color', 29 | ); 30 | } 31 | } 32 | 33 | class SeedColorPickerDialog extends StatelessWidget { 34 | const SeedColorPickerDialog({super.key}); 35 | 36 | @override 37 | Widget build(BuildContext context) { 38 | return const AlertDialog( 39 | content: SingleChildScrollView( 40 | child: Column( 41 | children: [ 42 | SeedColorPicker( 43 | wheelDiameter: 160, 44 | colorDimension: 32, 45 | ), 46 | SeedColorHistoryPanel(), 47 | ], 48 | ), 49 | ), 50 | ); 51 | } 52 | } 53 | 54 | class SeedColorPicker extends ConsumerWidget { 55 | const SeedColorPicker({ 56 | super.key, 57 | this.wheelDiameter = 320, 58 | this.colorDimension = 48, 59 | }); 60 | 61 | final double wheelDiameter; 62 | final double colorDimension; 63 | 64 | @override 65 | Widget build(BuildContext context, WidgetRef ref) { 66 | return Column( 67 | children: [ 68 | const Gap(p16), 69 | Text( 70 | 'Seed Color', 71 | style: context.titleMedium, 72 | ), 73 | ColorPicker( 74 | color: ref.watch(currentSeedColorNotifierProvider), 75 | onColorChanged: (color) async { 76 | await ref 77 | .read(currentSeedColorNotifierProvider.notifier) 78 | .updateValue(color); 79 | }, 80 | width: colorDimension, 81 | height: colorDimension, 82 | borderRadius: 25, 83 | subheading: Text( 84 | 'Select color shade', 85 | style: context.titleSmall, 86 | ), 87 | wheelDiameter: wheelDiameter, 88 | wheelSubheading: Text( 89 | 'Selected color and its shades', 90 | style: context.titleSmall, 91 | ), 92 | showMaterialName: true, 93 | showColorName: true, 94 | showColorCode: true, 95 | copyPasteBehavior: const ColorPickerCopyPasteBehavior( 96 | longPressMenu: true, 97 | ), 98 | materialNameTextStyle: context.bodySmall, 99 | colorNameTextStyle: context.bodySmall, 100 | colorCodeTextStyle: context.bodySmall, 101 | pickersEnabled: const { 102 | ColorPickerType.primary: true, 103 | ColorPickerType.accent: true, 104 | ColorPickerType.wheel: true, 105 | }, 106 | ), 107 | ], 108 | ); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /lib/feature/home/ui/page/home_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 3 | import 'package:recase/recase.dart'; 4 | 5 | import '../../../../core/ui/component/layout.dart'; 6 | import '../../../color/ui/component/color_scheme.dart'; 7 | import '../../../color/ui/component/tonal_palette.dart'; 8 | import '../../../seed_color_history/ui/component/popup_menu.dart'; 9 | import '../../../theme_mode/state/current_brightness.dart'; 10 | import '../../../theme_mode/ui/component/toggle_theme_mode_button.dart'; 11 | import 'component/home_drawer.dart'; 12 | import 'component/home_panel.dart'; 13 | import 'component/home_title.dart'; 14 | 15 | const homeBodyMaxWidth = 1200.0; 16 | 17 | class HomePage extends StatelessWidget { 18 | const HomePage({super.key}); 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | return Scaffold( 23 | appBar: AppBar( 24 | title: const HomeTitle(), 25 | actions: const [ 26 | ToggleThemeModeButton(), 27 | SeedColorHistoryPopupMenuButton(), 28 | ], 29 | ), 30 | body: const _Body(), 31 | drawer: context.isShowDrawer ? const HomeDrawer() : null, 32 | ); 33 | } 34 | } 35 | 36 | class _Body extends StatelessWidget { 37 | const _Body(); 38 | 39 | @override 40 | Widget build(BuildContext context) { 41 | if (context.isShowDrawer) { 42 | return const _Content(); 43 | } 44 | 45 | return const Row( 46 | crossAxisAlignment: CrossAxisAlignment.start, 47 | children: [ 48 | HomeDrawerContent(), 49 | Expanded( 50 | child: _Content(), 51 | ), 52 | ], 53 | ); 54 | } 55 | } 56 | 57 | class _Content extends StatelessWidget { 58 | const _Content(); 59 | 60 | @override 61 | Widget build(BuildContext context) { 62 | return SingleChildScrollView( 63 | child: Center( 64 | child: ConstrainedBox( 65 | constraints: const BoxConstraints( 66 | maxWidth: homeBodyMaxWidth, 67 | ), 68 | child: const Column( 69 | children: [ 70 | _TonalPalettesPanel(), 71 | Divider( 72 | indent: p8, 73 | endIndent: p8, 74 | ), 75 | _ColorSchemesPanel(), 76 | ], 77 | ), 78 | ), 79 | ), 80 | ); 81 | } 82 | } 83 | 84 | class _TonalPalettesPanel extends StatelessWidget { 85 | const _TonalPalettesPanel(); 86 | 87 | @override 88 | Widget build(BuildContext context) { 89 | return const HomePanel( 90 | title: 'Tonal Palettes', 91 | child: TonalPalettes(), 92 | ); 93 | } 94 | } 95 | 96 | class _ColorSchemesPanel extends ConsumerWidget { 97 | const _ColorSchemesPanel(); 98 | 99 | @override 100 | Widget build(BuildContext context, WidgetRef ref) { 101 | final brightness = ref.watch(currentBrightnessProvider); 102 | return HomePanel( 103 | title: brightness.title, 104 | child: const ColorSchemes(), 105 | ); 106 | } 107 | } 108 | 109 | extension on Brightness { 110 | String get title => '${ReCase(name).titleCase} Color Scheme'; 111 | } 112 | 113 | extension on BuildContext { 114 | bool get isShowDrawer { 115 | final screenWidth = MediaQuery.of(this).size.width; 116 | return screenWidth < (homeBodyMaxWidth + homeDrawerWidth); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /lib/feature/launcher/component/widget_ref_x.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 2 | 3 | import '../../../core/ui/component/widget_ref_x.dart'; 4 | import '../use_case/launch_url.dart'; 5 | 6 | extension LauncherWidgetRefX on WidgetRef { 7 | void listenLauncher() { 8 | listen( 9 | launchUrlUseCaseProvider, 10 | (prev, next) async { 11 | await next.when( 12 | data: (data) {}, 13 | error: (err, stack) async { 14 | await showErrorDialog(error: err.launcherErrorMessage); 15 | }, 16 | loading: () {}, 17 | ); 18 | }, 19 | onError: (err, stack) async { 20 | await showErrorDialog(error: err.launcherErrorMessage); 21 | }, 22 | ); 23 | } 24 | } 25 | 26 | extension on Object { 27 | String get launcherErrorMessage { 28 | if (this is LaunchUrlUseCaseException) { 29 | final error = this as LaunchUrlUseCaseException; 30 | return '${error.state.url} を開くことができませんでした。'; 31 | } 32 | return toString(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/feature/launcher/use_case/launch_url.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/services.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 4 | import 'package:url_launcher/url_launcher.dart' as url_launcher; 5 | 6 | import '../../../core/exception/app_exception.dart'; 7 | import '../../../core/use_case/use_case.dart'; 8 | import '../../../util/logger.dart'; 9 | 10 | part 'launch_url.freezed.dart'; 11 | part 'launch_url.g.dart'; 12 | 13 | @riverpod 14 | class LaunchUrlUseCase extends _$LaunchUrlUseCase with UseCase { 15 | @override 16 | FutureOr build() => 17 | buildInternal(LaunchUrlUseCaseState.empty); 18 | 19 | Future invoke( 20 | Uri url, { 21 | url_launcher.LaunchMode mode = url_launcher.LaunchMode.platformDefault, 22 | }) => 23 | invokeInternal(() async { 24 | final state = LaunchUrlUseCaseState(url: url, mode: mode); 25 | try { 26 | await url_launcher.launchUrl(state.url, mode: state.mode); 27 | logger.i('Successful launch: url = ${state.url}'); 28 | return state; 29 | } on FormatException catch (e, s) { 30 | logger.e( 31 | 'Can\'t parse url: url = ${state.url}', 32 | error: e, 33 | stackTrace: s, 34 | ); 35 | throw LaunchUrlUseCaseException(state, e, s); 36 | } on PlatformException catch (e, s) { 37 | logger.w( 38 | 'Failure launch: url = ${state.url}', 39 | error: e, 40 | stackTrace: s, 41 | ); 42 | throw LaunchUrlUseCaseException(state, e, s); 43 | // ignore: avoid_catching_errors 44 | } on ArgumentError catch (e, s) { 45 | logger.w( 46 | 'Failure launch: url = ${state.url}', 47 | error: e, 48 | stackTrace: s, 49 | ); 50 | throw LaunchUrlUseCaseException(state, e, s); 51 | } 52 | }); 53 | } 54 | 55 | @freezed 56 | class LaunchUrlUseCaseState with _$LaunchUrlUseCaseState { 57 | const factory LaunchUrlUseCaseState({ 58 | required Uri url, 59 | required url_launcher.LaunchMode mode, 60 | }) = _LaunchUrlUseCaseState; 61 | 62 | factory LaunchUrlUseCaseState.empty() => LaunchUrlUseCaseState( 63 | url: Uri(), 64 | mode: url_launcher.LaunchMode.platformDefault, 65 | ); 66 | } 67 | 68 | class LaunchUrlUseCaseException extends AppException { 69 | const LaunchUrlUseCaseException( 70 | this.state, [ 71 | this.details, 72 | this.stacktrace, 73 | ]) : super('LaunchUrlUseCaseException'); 74 | 75 | /// 状態 76 | final LaunchUrlUseCaseState state; 77 | 78 | /// 例外 79 | final dynamic details; 80 | 81 | /// スタックトレース 82 | final StackTrace? stacktrace; 83 | 84 | @override 85 | String toString() => 'LaunchUrlUseCaseException: $message, ' 86 | '$state, details = $details'; 87 | } 88 | -------------------------------------------------------------------------------- /lib/feature/launcher/use_case/launch_url.freezed.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | // GENERATED CODE - DO NOT MODIFY BY HAND 3 | // ignore_for_file: type=lint 4 | // 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 5 | 6 | part of 'launch_url.dart'; 7 | 8 | // ************************************************************************** 9 | // FreezedGenerator 10 | // ************************************************************************** 11 | 12 | T _$identity(T value) => value; 13 | 14 | final _privateConstructorUsedError = UnsupportedError( 15 | '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#adding-getters-and-methods-to-our-models'); 16 | 17 | /// @nodoc 18 | mixin _$LaunchUrlUseCaseState { 19 | Uri get url => throw _privateConstructorUsedError; 20 | url_launcher.LaunchMode get mode => throw _privateConstructorUsedError; 21 | 22 | /// Create a copy of LaunchUrlUseCaseState 23 | /// with the given fields replaced by the non-null parameter values. 24 | @JsonKey(includeFromJson: false, includeToJson: false) 25 | $LaunchUrlUseCaseStateCopyWith get copyWith => 26 | throw _privateConstructorUsedError; 27 | } 28 | 29 | /// @nodoc 30 | abstract class $LaunchUrlUseCaseStateCopyWith<$Res> { 31 | factory $LaunchUrlUseCaseStateCopyWith(LaunchUrlUseCaseState value, 32 | $Res Function(LaunchUrlUseCaseState) then) = 33 | _$LaunchUrlUseCaseStateCopyWithImpl<$Res, LaunchUrlUseCaseState>; 34 | @useResult 35 | $Res call({Uri url, url_launcher.LaunchMode mode}); 36 | } 37 | 38 | /// @nodoc 39 | class _$LaunchUrlUseCaseStateCopyWithImpl<$Res, 40 | $Val extends LaunchUrlUseCaseState> 41 | implements $LaunchUrlUseCaseStateCopyWith<$Res> { 42 | _$LaunchUrlUseCaseStateCopyWithImpl(this._value, this._then); 43 | 44 | // ignore: unused_field 45 | final $Val _value; 46 | // ignore: unused_field 47 | final $Res Function($Val) _then; 48 | 49 | /// Create a copy of LaunchUrlUseCaseState 50 | /// with the given fields replaced by the non-null parameter values. 51 | @pragma('vm:prefer-inline') 52 | @override 53 | $Res call({ 54 | Object? url = null, 55 | Object? mode = null, 56 | }) { 57 | return _then(_value.copyWith( 58 | url: null == url 59 | ? _value.url 60 | : url // ignore: cast_nullable_to_non_nullable 61 | as Uri, 62 | mode: null == mode 63 | ? _value.mode 64 | : mode // ignore: cast_nullable_to_non_nullable 65 | as url_launcher.LaunchMode, 66 | ) as $Val); 67 | } 68 | } 69 | 70 | /// @nodoc 71 | abstract class _$$LaunchUrlUseCaseStateImplCopyWith<$Res> 72 | implements $LaunchUrlUseCaseStateCopyWith<$Res> { 73 | factory _$$LaunchUrlUseCaseStateImplCopyWith( 74 | _$LaunchUrlUseCaseStateImpl value, 75 | $Res Function(_$LaunchUrlUseCaseStateImpl) then) = 76 | __$$LaunchUrlUseCaseStateImplCopyWithImpl<$Res>; 77 | @override 78 | @useResult 79 | $Res call({Uri url, url_launcher.LaunchMode mode}); 80 | } 81 | 82 | /// @nodoc 83 | class __$$LaunchUrlUseCaseStateImplCopyWithImpl<$Res> 84 | extends _$LaunchUrlUseCaseStateCopyWithImpl<$Res, 85 | _$LaunchUrlUseCaseStateImpl> 86 | implements _$$LaunchUrlUseCaseStateImplCopyWith<$Res> { 87 | __$$LaunchUrlUseCaseStateImplCopyWithImpl(_$LaunchUrlUseCaseStateImpl _value, 88 | $Res Function(_$LaunchUrlUseCaseStateImpl) _then) 89 | : super(_value, _then); 90 | 91 | /// Create a copy of LaunchUrlUseCaseState 92 | /// with the given fields replaced by the non-null parameter values. 93 | @pragma('vm:prefer-inline') 94 | @override 95 | $Res call({ 96 | Object? url = null, 97 | Object? mode = null, 98 | }) { 99 | return _then(_$LaunchUrlUseCaseStateImpl( 100 | url: null == url 101 | ? _value.url 102 | : url // ignore: cast_nullable_to_non_nullable 103 | as Uri, 104 | mode: null == mode 105 | ? _value.mode 106 | : mode // ignore: cast_nullable_to_non_nullable 107 | as url_launcher.LaunchMode, 108 | )); 109 | } 110 | } 111 | 112 | /// @nodoc 113 | 114 | class _$LaunchUrlUseCaseStateImpl implements _LaunchUrlUseCaseState { 115 | const _$LaunchUrlUseCaseStateImpl({required this.url, required this.mode}); 116 | 117 | @override 118 | final Uri url; 119 | @override 120 | final url_launcher.LaunchMode mode; 121 | 122 | @override 123 | String toString() { 124 | return 'LaunchUrlUseCaseState(url: $url, mode: $mode)'; 125 | } 126 | 127 | @override 128 | bool operator ==(Object other) { 129 | return identical(this, other) || 130 | (other.runtimeType == runtimeType && 131 | other is _$LaunchUrlUseCaseStateImpl && 132 | (identical(other.url, url) || other.url == url) && 133 | (identical(other.mode, mode) || other.mode == mode)); 134 | } 135 | 136 | @override 137 | int get hashCode => Object.hash(runtimeType, url, mode); 138 | 139 | /// Create a copy of LaunchUrlUseCaseState 140 | /// with the given fields replaced by the non-null parameter values. 141 | @JsonKey(includeFromJson: false, includeToJson: false) 142 | @override 143 | @pragma('vm:prefer-inline') 144 | _$$LaunchUrlUseCaseStateImplCopyWith<_$LaunchUrlUseCaseStateImpl> 145 | get copyWith => __$$LaunchUrlUseCaseStateImplCopyWithImpl< 146 | _$LaunchUrlUseCaseStateImpl>(this, _$identity); 147 | } 148 | 149 | abstract class _LaunchUrlUseCaseState implements LaunchUrlUseCaseState { 150 | const factory _LaunchUrlUseCaseState( 151 | {required final Uri url, 152 | required final url_launcher.LaunchMode mode}) = 153 | _$LaunchUrlUseCaseStateImpl; 154 | 155 | @override 156 | Uri get url; 157 | @override 158 | url_launcher.LaunchMode get mode; 159 | 160 | /// Create a copy of LaunchUrlUseCaseState 161 | /// with the given fields replaced by the non-null parameter values. 162 | @override 163 | @JsonKey(includeFromJson: false, includeToJson: false) 164 | _$$LaunchUrlUseCaseStateImplCopyWith<_$LaunchUrlUseCaseStateImpl> 165 | get copyWith => throw _privateConstructorUsedError; 166 | } 167 | -------------------------------------------------------------------------------- /lib/feature/launcher/use_case/launch_url.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'launch_url.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$launchUrlUseCaseHash() => r'be46c6b6ce92fae7a9b1e9f0d99b3b1914d142e0'; 10 | 11 | /// See also [LaunchUrlUseCase]. 12 | @ProviderFor(LaunchUrlUseCase) 13 | final launchUrlUseCaseProvider = AutoDisposeAsyncNotifierProvider< 14 | LaunchUrlUseCase, LaunchUrlUseCaseState?>.internal( 15 | LaunchUrlUseCase.new, 16 | name: r'launchUrlUseCaseProvider', 17 | debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') 18 | ? null 19 | : _$launchUrlUseCaseHash, 20 | dependencies: null, 21 | allTransitiveDependencies: null, 22 | ); 23 | 24 | typedef _$LaunchUrlUseCase = AutoDisposeAsyncNotifier; 25 | // ignore_for_file: type=lint 26 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package 27 | -------------------------------------------------------------------------------- /lib/feature/seed_color_history/entity/seed_color_history.dart: -------------------------------------------------------------------------------- 1 | import 'package:collection/collection.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:hexcolor/hexcolor.dart'; 4 | import 'package:hive/hive.dart'; 5 | 6 | part 'seed_color_history.g.dart'; 7 | 8 | @HiveType(typeId: 1) 9 | class SeedColorHistory extends HiveObject { 10 | SeedColorHistory({ 11 | required this.hex, 12 | required this.name, 13 | required this.variantName, 14 | }); 15 | 16 | @HiveField(0) 17 | final String hex; 18 | 19 | @HiveField(1) 20 | final String name; 21 | 22 | @HiveField(2) 23 | final DateTime createdAt = DateTime.now(); 24 | 25 | @HiveField(3) 26 | final String? variantName; 27 | 28 | Color get color => HexColor(hex); 29 | 30 | DynamicSchemeVariant get variant => 31 | DynamicSchemeVariant.values 32 | .firstWhereOrNull((e) => e.name == variantName) ?? 33 | DynamicSchemeVariant.rainbow; 34 | } 35 | -------------------------------------------------------------------------------- /lib/feature/seed_color_history/entity/seed_color_history.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'seed_color_history.dart'; 4 | 5 | // ************************************************************************** 6 | // TypeAdapterGenerator 7 | // ************************************************************************** 8 | 9 | class SeedColorHistoryAdapter extends TypeAdapter { 10 | @override 11 | final int typeId = 1; 12 | 13 | @override 14 | SeedColorHistory read(BinaryReader reader) { 15 | final numOfFields = reader.readByte(); 16 | final fields = { 17 | for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), 18 | }; 19 | return SeedColorHistory( 20 | hex: fields[0] as String, 21 | name: fields[1] as String, 22 | variantName: fields[3] as String?, 23 | ); 24 | } 25 | 26 | @override 27 | void write(BinaryWriter writer, SeedColorHistory obj) { 28 | writer 29 | ..writeByte(4) 30 | ..writeByte(0) 31 | ..write(obj.hex) 32 | ..writeByte(1) 33 | ..write(obj.name) 34 | ..writeByte(2) 35 | ..write(obj.createdAt) 36 | ..writeByte(3) 37 | ..write(obj.variantName); 38 | } 39 | 40 | @override 41 | int get hashCode => typeId.hashCode; 42 | 43 | @override 44 | bool operator ==(Object other) => 45 | identical(this, other) || 46 | other is SeedColorHistoryAdapter && 47 | runtimeType == other.runtimeType && 48 | typeId == other.typeId; 49 | } 50 | -------------------------------------------------------------------------------- /lib/feature/seed_color_history/entity/seed_color_history_collection.dart: -------------------------------------------------------------------------------- 1 | import 'package:hive/hive.dart'; 2 | 3 | import 'seed_color_history.dart'; 4 | 5 | part 'seed_color_history_collection.g.dart'; 6 | 7 | @HiveType(typeId: 0) 8 | class SeedColorHistoryCollection extends HiveObject { 9 | SeedColorHistoryCollection({ 10 | this.histories = const [], 11 | }); 12 | 13 | static const boxName = 'seedColorHistoryCollection'; 14 | static const keyName = 'collection'; 15 | 16 | @HiveField(0) 17 | final List histories; 18 | } 19 | -------------------------------------------------------------------------------- /lib/feature/seed_color_history/entity/seed_color_history_collection.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'seed_color_history_collection.dart'; 4 | 5 | // ************************************************************************** 6 | // TypeAdapterGenerator 7 | // ************************************************************************** 8 | 9 | class SeedColorHistoryCollectionAdapter 10 | extends TypeAdapter { 11 | @override 12 | final int typeId = 0; 13 | 14 | @override 15 | SeedColorHistoryCollection read(BinaryReader reader) { 16 | final numOfFields = reader.readByte(); 17 | final fields = { 18 | for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), 19 | }; 20 | return SeedColorHistoryCollection( 21 | histories: (fields[0] as List).cast(), 22 | ); 23 | } 24 | 25 | @override 26 | void write(BinaryWriter writer, SeedColorHistoryCollection obj) { 27 | writer 28 | ..writeByte(1) 29 | ..writeByte(0) 30 | ..write(obj.histories); 31 | } 32 | 33 | @override 34 | int get hashCode => typeId.hashCode; 35 | 36 | @override 37 | bool operator ==(Object other) => 38 | identical(this, other) || 39 | other is SeedColorHistoryCollectionAdapter && 40 | runtimeType == other.runtimeType && 41 | typeId == other.typeId; 42 | } 43 | -------------------------------------------------------------------------------- /lib/feature/seed_color_history/state/seed_color_history_collection.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 4 | import 'package:hive/hive.dart'; 5 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 6 | 7 | import '../entity/seed_color_history_collection.dart'; 8 | 9 | part 'seed_color_history_collection.g.dart'; 10 | 11 | @riverpod 12 | Box seedColorHistoryCollectionBox(Ref ref) { 13 | throw UnimplementedError(); 14 | } 15 | 16 | @riverpod 17 | class SeedColorHistoryCollectionNotifier 18 | extends _$SeedColorHistoryCollectionNotifier { 19 | late final StreamSubscription _subscription; 20 | @override 21 | SeedColorHistoryCollection build() { 22 | final box = ref.watch(seedColorHistoryCollectionBoxProvider); 23 | _subscription = box 24 | .watch(key: SeedColorHistoryCollection.keyName) 25 | .map( 26 | (event) => event.value as SeedColorHistoryCollection?, 27 | ) 28 | .listen((collection) { 29 | if (collection != null) { 30 | state = collection; 31 | } 32 | }); 33 | ref.onDispose(_subscription.cancel); 34 | return box.get(SeedColorHistoryCollection.keyName) ?? 35 | SeedColorHistoryCollection(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/feature/seed_color_history/state/seed_color_history_collection.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'seed_color_history_collection.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$seedColorHistoryCollectionBoxHash() => 10 | r'a197c0151d7605cbde3543c83ad40112fa95c4b7'; 11 | 12 | /// See also [seedColorHistoryCollectionBox]. 13 | @ProviderFor(seedColorHistoryCollectionBox) 14 | final seedColorHistoryCollectionBoxProvider = 15 | AutoDisposeProvider>.internal( 16 | seedColorHistoryCollectionBox, 17 | name: r'seedColorHistoryCollectionBoxProvider', 18 | debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') 19 | ? null 20 | : _$seedColorHistoryCollectionBoxHash, 21 | dependencies: null, 22 | allTransitiveDependencies: null, 23 | ); 24 | 25 | @Deprecated('Will be removed in 3.0. Use Ref instead') 26 | // ignore: unused_element 27 | typedef SeedColorHistoryCollectionBoxRef 28 | = AutoDisposeProviderRef>; 29 | String _$seedColorHistoryCollectionNotifierHash() => 30 | r'cfd170babd7444b441a65dc38be8996a8676c745'; 31 | 32 | /// See also [SeedColorHistoryCollectionNotifier]. 33 | @ProviderFor(SeedColorHistoryCollectionNotifier) 34 | final seedColorHistoryCollectionNotifierProvider = AutoDisposeNotifierProvider< 35 | SeedColorHistoryCollectionNotifier, SeedColorHistoryCollection>.internal( 36 | SeedColorHistoryCollectionNotifier.new, 37 | name: r'seedColorHistoryCollectionNotifierProvider', 38 | debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') 39 | ? null 40 | : _$seedColorHistoryCollectionNotifierHash, 41 | dependencies: null, 42 | allTransitiveDependencies: null, 43 | ); 44 | 45 | typedef _$SeedColorHistoryCollectionNotifier 46 | = AutoDisposeNotifier; 47 | // ignore_for_file: type=lint 48 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package 49 | -------------------------------------------------------------------------------- /lib/feature/seed_color_history/ui/component/dynamic_scheme_variant.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 3 | import 'package:gap/gap.dart'; 4 | 5 | import '../../../../core/ui/component/layout.dart'; 6 | import '../../../../core/ui/component/material.dart'; 7 | import '../../../color/state/current_dynamic_scheme_variant.dart'; 8 | 9 | class DynamicSchemeVariantChips extends ConsumerWidget { 10 | const DynamicSchemeVariantChips({super.key}); 11 | 12 | @override 13 | Widget build(BuildContext context, WidgetRef ref) { 14 | final currentVariant = 15 | ref.watch(currentDynamicSchemeVariantNotifierProvider); 16 | return Column( 17 | children: [ 18 | const Gap(p8), 19 | Text( 20 | 'Dynamic Scheme Variant', 21 | style: context.titleMedium, 22 | ), 23 | const Gap(p16), 24 | Wrap( 25 | children: [ 26 | ...DynamicSchemeVariant.values.map( 27 | (variant) => _Chip( 28 | variant: variant, 29 | selected: variant == currentVariant, 30 | ), 31 | ), 32 | ], 33 | ), 34 | const Gap(p32), 35 | ], 36 | ); 37 | } 38 | } 39 | 40 | class _Chip extends ConsumerWidget { 41 | const _Chip({ 42 | required this.variant, 43 | required this.selected, 44 | }); 45 | 46 | final DynamicSchemeVariant variant; 47 | final bool selected; 48 | 49 | @override 50 | Widget build(BuildContext context, WidgetRef ref) { 51 | return Padding( 52 | padding: const EdgeInsets.all(2), 53 | child: FilterChip( 54 | onSelected: (value) { 55 | if (value) { 56 | ref 57 | .read(currentDynamicSchemeVariantNotifierProvider.notifier) 58 | .updateValue(variant); 59 | } 60 | }, 61 | selected: selected, 62 | label: Text(variant.name), 63 | tooltip: variant.description, 64 | ), 65 | ); 66 | } 67 | } 68 | 69 | extension on DynamicSchemeVariant { 70 | String get description => switch (this) { 71 | DynamicSchemeVariant.tonalSpot => 72 | '''Default for Material theme colors. Builds pastel palettes with a low chroma.''', 73 | DynamicSchemeVariant.fidelity => 74 | '''The resulting color palettes match seed color, even if the seed color is very bright (high chroma).''', 75 | DynamicSchemeVariant.monochrome => 76 | '''All colors are grayscale, no chroma.''', 77 | DynamicSchemeVariant.neutral => 78 | '''Close to grayscale, a hint of chroma.''', 79 | DynamicSchemeVariant.vibrant => 80 | '''Pastel colors, high chroma palettes. The primary palette's chroma is at maximum. Use `fidelity` instead if tokens should alter their tone to match the palette vibrancy.''', 81 | DynamicSchemeVariant.expressive => 82 | '''Pastel colors, medium chroma palettes. The primary palette's hue is different from the seed color, for variety.''', 83 | DynamicSchemeVariant.content => 84 | '''Almost identical to `fidelity`. Tokens and palettes match the seed color. primaryContainer is the seed color, adjusted to ensure contrast with surfaces. The tertiary palette is analogue of the seed color.''', 85 | DynamicSchemeVariant.rainbow => 86 | '''A playful theme - the seed color's hue does not appear in the theme.''', 87 | DynamicSchemeVariant.fruitSalad => 88 | '''A playful theme - the seed color's hue does not appear in the theme.''', 89 | }; 90 | } 91 | -------------------------------------------------------------------------------- /lib/feature/seed_color_history/ui/component/popup_menu.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 3 | import 'package:gap/gap.dart'; 4 | 5 | import '../../../../core/ui/component/layout.dart'; 6 | import '../../../../core/ui/component/widget_ref_x.dart'; 7 | import '../../use_case/add_seed_color_history.dart'; 8 | import 'seed_color_history.dart'; 9 | 10 | class SeedColorHistoryPopupMenuButton extends ConsumerWidget { 11 | const SeedColorHistoryPopupMenuButton({super.key}); 12 | 13 | @override 14 | Widget build(BuildContext context, WidgetRef ref) { 15 | ref.listenAsync( 16 | addSeedColorHistoryUseCaseProvider, 17 | success: (_) { 18 | ref.showSnackBar( 19 | const SnackBar( 20 | content: Text('Saved.'), 21 | width: snackBarWidth, 22 | ), 23 | ); 24 | }, 25 | ); 26 | return PopupMenuButton( 27 | itemBuilder: (context) => [ 28 | ..._MenuItem.values.map( 29 | (e) => PopupMenuItem( 30 | onTap: () => ref.onTapMenuItem(e), 31 | child: Row( 32 | children: [ 33 | Icon(e.icon), 34 | const Gap(p16), 35 | Text(e.label), 36 | ], 37 | ), 38 | ), 39 | ), 40 | ], 41 | ); 42 | } 43 | } 44 | 45 | enum _MenuItem { 46 | load, 47 | save, 48 | ; 49 | 50 | String get label => switch (this) { 51 | _MenuItem.load => 'LOAD', 52 | _MenuItem.save => 'SAVE', 53 | }; 54 | 55 | IconData get icon => switch (this) { 56 | _MenuItem.load => Icons.file_open, 57 | _MenuItem.save => Icons.save, 58 | }; 59 | } 60 | 61 | extension on WidgetRef { 62 | Future onTapMenuItem(_MenuItem item) async { 63 | switch (item) { 64 | case _MenuItem.load: 65 | await showModalBottomSheet( 66 | context: context, 67 | builder: (context) => const SeedColorHistoryBottomSheet(), 68 | ); 69 | case _MenuItem.save: 70 | await showDialog( 71 | context: context, 72 | builder: (context) => const _SaveDialog(), 73 | ); 74 | } 75 | } 76 | } 77 | 78 | class _SaveDialog extends ConsumerStatefulWidget { 79 | const _SaveDialog(); 80 | 81 | @override 82 | ConsumerState<_SaveDialog> createState() => _SaveDialogState(); 83 | } 84 | 85 | class _SaveDialogState extends ConsumerState<_SaveDialog> { 86 | final _textController = TextEditingController(); 87 | 88 | @override 89 | void initState() { 90 | super.initState(); 91 | _textController.addListener(() { 92 | setState(() {}); 93 | }); 94 | } 95 | 96 | @override 97 | void dispose() { 98 | _textController.dispose(); 99 | super.dispose(); 100 | } 101 | 102 | @override 103 | Widget build(BuildContext context) { 104 | final isValid = _textController.text.isNotEmpty; 105 | return AlertDialog( 106 | title: const Text('Save Seed Color'), 107 | content: TextField( 108 | controller: _textController, 109 | decoration: const InputDecoration( 110 | hintText: 'Input name', 111 | ), 112 | ), 113 | actions: [ 114 | TextButton( 115 | onPressed: () => Navigator.of(context).pop(), 116 | child: const Text('Cancel'), 117 | ), 118 | TextButton( 119 | onPressed: isValid 120 | ? () async { 121 | final name = _textController.text; 122 | await ref 123 | .read(addSeedColorHistoryUseCaseProvider.notifier) 124 | .invoke(name: name); 125 | Navigator.of(context).pop(); 126 | } 127 | : null, 128 | child: const Text('Save'), 129 | ), 130 | ], 131 | ); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /lib/feature/seed_color_history/ui/component/seed_color_history.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 3 | import 'package:gap/gap.dart'; 4 | 5 | import '../../../../core/ui/component/bottom_sheet.dart'; 6 | import '../../../../core/ui/component/material.dart'; 7 | import '../../../color/state/current_dynamic_scheme_variant.dart'; 8 | import '../../../color/state/current_seed_color.dart'; 9 | import '../../entity/seed_color_history.dart'; 10 | import '../../state/seed_color_history_collection.dart'; 11 | import '../../use_case/delete_seed_color_history.dart'; 12 | 13 | class SeedColorHistoryBottomSheet extends StatelessWidget { 14 | const SeedColorHistoryBottomSheet({super.key}); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return const BottomSheetContainer( 19 | child: SeedColorHistoryPanel(), 20 | ); 21 | } 22 | } 23 | 24 | class SeedColorHistoryPanel extends ConsumerWidget { 25 | const SeedColorHistoryPanel({super.key}); 26 | 27 | @override 28 | Widget build(BuildContext context, WidgetRef ref) { 29 | final collection = ref.watch(seedColorHistoryCollectionNotifierProvider); 30 | return Column( 31 | children: [ 32 | SizedBox( 33 | width: double.infinity, 34 | height: 44, 35 | child: Stack( 36 | alignment: AlignmentDirectional.center, 37 | children: [ 38 | Text( 39 | 'Seed Color History', 40 | style: context.titleSmall, 41 | textAlign: TextAlign.center, 42 | ), 43 | ], 44 | ), 45 | ), 46 | const Gap(8), 47 | ...collection.histories.map( 48 | (e) => _ListTile( 49 | history: e, 50 | ), 51 | ), 52 | if (collection.histories.isEmpty) 53 | Text( 54 | 'NO HISTORY', 55 | style: context.caption, 56 | ), 57 | const Gap(16), 58 | ], 59 | ); 60 | } 61 | } 62 | 63 | class _ListTile extends ConsumerWidget { 64 | const _ListTile({ 65 | required this.history, 66 | }); 67 | 68 | final SeedColorHistory history; 69 | 70 | @override 71 | Widget build(BuildContext context, WidgetRef ref) { 72 | return ListTile( 73 | onTap: () { 74 | ref 75 | .read(currentSeedColorNotifierProvider.notifier) 76 | .updateValue(history.color); 77 | ref 78 | .read(currentDynamicSchemeVariantNotifierProvider.notifier) 79 | .updateValue(history.variant); 80 | Navigator.of(context).pop(); 81 | }, 82 | leading: CircleAvatar( 83 | backgroundColor: history.color, 84 | ), 85 | title: Text(history.name), 86 | subtitle: Text(history.variant.name), 87 | trailing: _DeleteButton( 88 | history: history, 89 | ), 90 | ); 91 | } 92 | } 93 | 94 | class _DeleteButton extends ConsumerWidget { 95 | const _DeleteButton({ 96 | required this.history, 97 | }); 98 | 99 | final SeedColorHistory history; 100 | 101 | @override 102 | Widget build(BuildContext context, WidgetRef ref) { 103 | final useCaseState = ref.watch(deleteSeedColorHistoryUseCaseProvider); 104 | return IconButton( 105 | onPressed: useCaseState.isLoading 106 | ? null 107 | : () => ref 108 | .read(deleteSeedColorHistoryUseCaseProvider.notifier) 109 | .invoke(history: history), 110 | icon: const Icon(Icons.delete), 111 | tooltip: 'Delete', 112 | ); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /lib/feature/seed_color_history/use_case/add_seed_color_history.dart: -------------------------------------------------------------------------------- 1 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 2 | 3 | import '../../../core/ui/component/material.dart'; 4 | import '../../../core/use_case/use_case.dart'; 5 | import '../../color/state/current_dynamic_scheme_variant.dart'; 6 | import '../../color/state/current_seed_color.dart'; 7 | import '../entity/seed_color_history.dart'; 8 | import '../entity/seed_color_history_collection.dart'; 9 | import '../state/seed_color_history_collection.dart'; 10 | 11 | part 'add_seed_color_history.g.dart'; 12 | 13 | @riverpod 14 | class AddSeedColorHistoryUseCase extends _$AddSeedColorHistoryUseCase 15 | with UseCase { 16 | @override 17 | FutureOr build() => buildInternal(() => null); 18 | 19 | Future invoke({ 20 | required String name, 21 | }) => 22 | invokeInternal(() async { 23 | final collection = ref.read(seedColorHistoryCollectionNotifierProvider); 24 | final seedColor = ref.read(currentSeedColorNotifierProvider); 25 | final variant = ref.read(currentDynamicSchemeVariantNotifierProvider); 26 | final histories = [ 27 | SeedColorHistory( 28 | hex: seedColor.toHexString(), 29 | name: name, 30 | variantName: variant.name, 31 | ), 32 | ...collection.histories, 33 | ]; 34 | if (histories.length > 10) { 35 | histories.removeLast(); 36 | } 37 | 38 | await ref.read(seedColorHistoryCollectionBoxProvider).put( 39 | SeedColorHistoryCollection.keyName, 40 | SeedColorHistoryCollection( 41 | histories: histories, 42 | ), 43 | ); 44 | }); 45 | } 46 | -------------------------------------------------------------------------------- /lib/feature/seed_color_history/use_case/add_seed_color_history.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'add_seed_color_history.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$addSeedColorHistoryUseCaseHash() => 10 | r'2168da8bb39f39cf748b200c44c98667668b7fa7'; 11 | 12 | /// See also [AddSeedColorHistoryUseCase]. 13 | @ProviderFor(AddSeedColorHistoryUseCase) 14 | final addSeedColorHistoryUseCaseProvider = 15 | AutoDisposeAsyncNotifierProvider.internal( 16 | AddSeedColorHistoryUseCase.new, 17 | name: r'addSeedColorHistoryUseCaseProvider', 18 | debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') 19 | ? null 20 | : _$addSeedColorHistoryUseCaseHash, 21 | dependencies: null, 22 | allTransitiveDependencies: null, 23 | ); 24 | 25 | typedef _$AddSeedColorHistoryUseCase = AutoDisposeAsyncNotifier; 26 | // ignore_for_file: type=lint 27 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package 28 | -------------------------------------------------------------------------------- /lib/feature/seed_color_history/use_case/delete_seed_color_history.dart: -------------------------------------------------------------------------------- 1 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 2 | 3 | import '../../../core/use_case/use_case.dart'; 4 | import '../entity/seed_color_history.dart'; 5 | import '../entity/seed_color_history_collection.dart'; 6 | import '../state/seed_color_history_collection.dart'; 7 | 8 | part 'delete_seed_color_history.g.dart'; 9 | 10 | @riverpod 11 | class DeleteSeedColorHistoryUseCase extends _$DeleteSeedColorHistoryUseCase 12 | with UseCase { 13 | @override 14 | FutureOr build() => buildInternal(() => null); 15 | 16 | Future invoke({ 17 | required SeedColorHistory history, 18 | }) => 19 | invokeInternal(() async { 20 | final histories = 21 | ref.read(seedColorHistoryCollectionNotifierProvider).histories; 22 | final updatedHistories = histories.toList()..remove(history); 23 | 24 | await ref.read(seedColorHistoryCollectionBoxProvider).put( 25 | SeedColorHistoryCollection.keyName, 26 | SeedColorHistoryCollection( 27 | histories: updatedHistories, 28 | ), 29 | ); 30 | }); 31 | } 32 | -------------------------------------------------------------------------------- /lib/feature/seed_color_history/use_case/delete_seed_color_history.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'delete_seed_color_history.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$deleteSeedColorHistoryUseCaseHash() => 10 | r'e454527324e20fa96bdedbaa2378fafdd01d5973'; 11 | 12 | /// See also [DeleteSeedColorHistoryUseCase]. 13 | @ProviderFor(DeleteSeedColorHistoryUseCase) 14 | final deleteSeedColorHistoryUseCaseProvider = AutoDisposeAsyncNotifierProvider< 15 | DeleteSeedColorHistoryUseCase, void>.internal( 16 | DeleteSeedColorHistoryUseCase.new, 17 | name: r'deleteSeedColorHistoryUseCaseProvider', 18 | debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') 19 | ? null 20 | : _$deleteSeedColorHistoryUseCaseHash, 21 | dependencies: null, 22 | allTransitiveDependencies: null, 23 | ); 24 | 25 | typedef _$DeleteSeedColorHistoryUseCase = AutoDisposeAsyncNotifier; 26 | // ignore_for_file: type=lint 27 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package 28 | -------------------------------------------------------------------------------- /lib/feature/theme_mode/state/current_brightness.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 3 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 4 | 5 | import 'current_theme_mode.dart'; 6 | 7 | part 'current_brightness.g.dart'; 8 | 9 | @riverpod 10 | Brightness currentBrightness(Ref ref) { 11 | final themeMode = ref.watch(currentThemeModeProvider); 12 | return themeMode == ThemeMode.light ? Brightness.light : Brightness.dark; 13 | } 14 | -------------------------------------------------------------------------------- /lib/feature/theme_mode/state/current_brightness.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'current_brightness.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$currentBrightnessHash() => r'0995059d65a90aaf8e9470b63080b4c790eae3b5'; 10 | 11 | /// See also [currentBrightness]. 12 | @ProviderFor(currentBrightness) 13 | final currentBrightnessProvider = AutoDisposeProvider.internal( 14 | currentBrightness, 15 | name: r'currentBrightnessProvider', 16 | debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') 17 | ? null 18 | : _$currentBrightnessHash, 19 | dependencies: null, 20 | allTransitiveDependencies: null, 21 | ); 22 | 23 | @Deprecated('Will be removed in 3.0. Use Ref instead') 24 | // ignore: unused_element 25 | typedef CurrentBrightnessRef = AutoDisposeProviderRef; 26 | // ignore_for_file: type=lint 27 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package 28 | -------------------------------------------------------------------------------- /lib/feature/theme_mode/state/current_theme_mode.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | 4 | import '../../../core/data/shared_preferences/shared_preferences.dart'; 5 | 6 | part 'current_theme_mode.g.dart'; 7 | 8 | @riverpod 9 | class CurrentThemeMode extends _$CurrentThemeMode { 10 | static const _key = 'themeMode'; 11 | 12 | @override 13 | ThemeMode build() { 14 | final prefs = ref.watch(sharedPreferencesProvider); 15 | return prefs.getString(_key)?.toThemeMode() ?? ThemeMode.light; 16 | } 17 | 18 | Future toggle() async { 19 | final newValue = state.toggle(); 20 | final prefs = ref.read(sharedPreferencesProvider); 21 | await prefs.setString(_key, newValue.name); 22 | state = newValue; 23 | } 24 | } 25 | 26 | extension on String { 27 | ThemeMode toThemeMode() => ThemeMode.values.firstWhere((e) => e.name == this); 28 | } 29 | 30 | extension on ThemeMode { 31 | ThemeMode toggle() { 32 | switch (this) { 33 | case ThemeMode.light: 34 | case ThemeMode.system: 35 | return ThemeMode.dark; 36 | case ThemeMode.dark: 37 | return ThemeMode.light; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/feature/theme_mode/state/current_theme_mode.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'current_theme_mode.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$currentThemeModeHash() => r'90d0e9db20b5acc5fc7a231041756d2f94c50759'; 10 | 11 | /// See also [CurrentThemeMode]. 12 | @ProviderFor(CurrentThemeMode) 13 | final currentThemeModeProvider = 14 | AutoDisposeNotifierProvider.internal( 15 | CurrentThemeMode.new, 16 | name: r'currentThemeModeProvider', 17 | debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') 18 | ? null 19 | : _$currentThemeModeHash, 20 | dependencies: null, 21 | allTransitiveDependencies: null, 22 | ); 23 | 24 | typedef _$CurrentThemeMode = AutoDisposeNotifier; 25 | // ignore_for_file: type=lint 26 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package 27 | -------------------------------------------------------------------------------- /lib/feature/theme_mode/ui/component/toggle_theme_mode_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 3 | 4 | import '../../state/current_theme_mode.dart'; 5 | 6 | class ToggleThemeModeButton extends ConsumerWidget { 7 | const ToggleThemeModeButton({super.key}); 8 | 9 | @override 10 | Widget build(BuildContext context, WidgetRef ref) { 11 | final themeMode = ref.watch(currentThemeModeProvider); 12 | return IconButton( 13 | onPressed: () => ref.read(currentThemeModeProvider.notifier).toggle(), 14 | icon: Icon(themeMode.icon), 15 | tooltip: 'Change ThemeMode', 16 | ); 17 | } 18 | } 19 | 20 | extension on ThemeMode { 21 | IconData get icon { 22 | switch (this) { 23 | case ThemeMode.light: 24 | case ThemeMode.system: 25 | return Icons.dark_mode; 26 | case ThemeMode.dark: 27 | return Icons.light_mode; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 3 | import 'package:hive_flutter/hive_flutter.dart'; 4 | import 'package:package_info_plus/package_info_plus.dart'; 5 | import 'package:shared_preferences/shared_preferences.dart'; 6 | 7 | import 'app.dart'; 8 | import 'core/data/package_info/package_info.dart'; 9 | import 'core/data/shared_preferences/shared_preferences.dart'; 10 | import 'feature/seed_color_history/entity/seed_color_history.dart'; 11 | import 'feature/seed_color_history/entity/seed_color_history_collection.dart'; 12 | import 'feature/seed_color_history/state/seed_color_history_collection.dart'; 13 | import 'util/provider_logger.dart'; 14 | 15 | Future main() async { 16 | WidgetsFlutterBinding.ensureInitialized(); 17 | 18 | final prefs = await SharedPreferences.getInstance(); 19 | 20 | await Hive.initFlutter(); 21 | Hive.registerAdapter(SeedColorHistoryCollectionAdapter()); 22 | Hive.registerAdapter(SeedColorHistoryAdapter()); 23 | final seedColorHistoryCollectionBox = 24 | await Hive.openBox( 25 | SeedColorHistoryCollection.boxName, 26 | ); 27 | final packageInfo = await PackageInfo.fromPlatform(); 28 | 29 | runApp( 30 | ProviderScope( 31 | overrides: [ 32 | sharedPreferencesProvider.overrideWithValue(prefs), 33 | packageInfoProvider.overrideWithValue(packageInfo), 34 | seedColorHistoryCollectionBoxProvider 35 | .overrideWithValue(seedColorHistoryCollectionBox), 36 | ], 37 | observers: [ 38 | ProviderLogger(), 39 | ], 40 | child: const App(), 41 | ), 42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /lib/util/assets/assets.gen.dart: -------------------------------------------------------------------------------- 1 | /// GENERATED CODE - DO NOT MODIFY BY HAND 2 | /// ***************************************************** 3 | /// FlutterGen 4 | /// ***************************************************** 5 | 6 | // coverage:ignore-file 7 | // ignore_for_file: type=lint 8 | // ignore_for_file: directives_ordering,unnecessary_import,implicit_dynamic_list_literal,deprecated_member_use 9 | 10 | import 'package:flutter/widgets.dart'; 11 | 12 | class $DartToolGen { 13 | const $DartToolGen(); 14 | 15 | /// File path: .dart_tool/version 16 | String get version => '.dart_tool/version'; 17 | 18 | /// List of all assets 19 | List get values => [version]; 20 | } 21 | 22 | class $AssetsImagesGen { 23 | const $AssetsImagesGen(); 24 | 25 | /// File path: assets/images/app-icon.png 26 | AssetGenImage get appIcon => 27 | const AssetGenImage('assets/images/app-icon.png'); 28 | 29 | /// List of all assets 30 | List get values => [appIcon]; 31 | } 32 | 33 | class Assets { 34 | Assets._(); 35 | 36 | static const $DartToolGen dartTool = $DartToolGen(); 37 | static const $AssetsImagesGen images = $AssetsImagesGen(); 38 | } 39 | 40 | class AssetGenImage { 41 | const AssetGenImage( 42 | this._assetName, { 43 | this.size, 44 | this.flavors = const {}, 45 | }); 46 | 47 | final String _assetName; 48 | 49 | final Size? size; 50 | final Set flavors; 51 | 52 | Image image({ 53 | Key? key, 54 | AssetBundle? bundle, 55 | ImageFrameBuilder? frameBuilder, 56 | ImageErrorWidgetBuilder? errorBuilder, 57 | String? semanticLabel, 58 | bool excludeFromSemantics = false, 59 | double? scale, 60 | double? width, 61 | double? height, 62 | Color? color, 63 | Animation? opacity, 64 | BlendMode? colorBlendMode, 65 | BoxFit? fit, 66 | AlignmentGeometry alignment = Alignment.center, 67 | ImageRepeat repeat = ImageRepeat.noRepeat, 68 | Rect? centerSlice, 69 | bool matchTextDirection = false, 70 | bool gaplessPlayback = true, 71 | bool isAntiAlias = false, 72 | String? package, 73 | FilterQuality filterQuality = FilterQuality.low, 74 | int? cacheWidth, 75 | int? cacheHeight, 76 | }) { 77 | return Image.asset( 78 | _assetName, 79 | key: key, 80 | bundle: bundle, 81 | frameBuilder: frameBuilder, 82 | errorBuilder: errorBuilder, 83 | semanticLabel: semanticLabel, 84 | excludeFromSemantics: excludeFromSemantics, 85 | scale: scale, 86 | width: width, 87 | height: height, 88 | color: color, 89 | opacity: opacity, 90 | colorBlendMode: colorBlendMode, 91 | fit: fit, 92 | alignment: alignment, 93 | repeat: repeat, 94 | centerSlice: centerSlice, 95 | matchTextDirection: matchTextDirection, 96 | gaplessPlayback: gaplessPlayback, 97 | isAntiAlias: isAntiAlias, 98 | package: package, 99 | filterQuality: filterQuality, 100 | cacheWidth: cacheWidth, 101 | cacheHeight: cacheHeight, 102 | ); 103 | } 104 | 105 | ImageProvider provider({ 106 | AssetBundle? bundle, 107 | String? package, 108 | }) { 109 | return AssetImage( 110 | _assetName, 111 | bundle: bundle, 112 | package: package, 113 | ); 114 | } 115 | 116 | String get path => _assetName; 117 | 118 | String get keyName => _assetName; 119 | } 120 | -------------------------------------------------------------------------------- /lib/util/extensions.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 2 | 3 | extension IntEx on int { 4 | /// 大文字、2桁の16進数の文字列に変換する 5 | String toHexString() => toRadixString(16).padLeft(2, '0').toUpperCase(); 6 | } 7 | 8 | extension AsyncValueXX on AsyncValue { 9 | /// エラーのみ処理する 10 | void whenError( 11 | void Function(Object error, StackTrace? stackTrace) error, 12 | ) { 13 | if (isLoading) { 14 | return; 15 | } 16 | return map( 17 | data: (_) {}, 18 | error: (e) => error(e.error, e.stackTrace), 19 | loading: (_) {}, 20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/util/logger.dart: -------------------------------------------------------------------------------- 1 | import 'package:roggle/roggle.dart'; 2 | 3 | const _loggerName = '[APP]'; 4 | 5 | final logger = Roggle( 6 | printer: SinglePrettyPrinter( 7 | loggerName: _loggerName, 8 | // error 以上のときはスタックトレースを出力する 9 | stackTraceLevel: Level.error, 10 | // ログが長くなるので非表示 11 | printCaller: false, 12 | ), 13 | ); 14 | -------------------------------------------------------------------------------- /lib/util/provider_logger.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 2 | 3 | import 'logger.dart'; 4 | 5 | const _logPrefix = '[RIVERPOD]'; 6 | 7 | /// Riverpod の各 Provider のライフサイクルをログ出力するためのオブザーバー 8 | /// 9 | /// ログ出力したい場合は Provider に name プロパティを設定すること 10 | class ProviderLogger extends ProviderObserver { 11 | @override 12 | void didAddProvider( 13 | ProviderBase provider, 14 | Object? value, 15 | ProviderContainer container, 16 | ) { 17 | final name = provider.name; 18 | if (name != null) { 19 | logger.t( 20 | '$_logPrefix[ADD] $name: value = ${value?.toDebugString()}', 21 | ); 22 | } 23 | } 24 | 25 | @override 26 | void providerDidFail( 27 | ProviderBase provider, 28 | Object error, 29 | StackTrace stackTrace, 30 | ProviderContainer container, 31 | ) { 32 | final name = provider.name; 33 | if (name != null) { 34 | logger.w( 35 | '$_logPrefix[FAIL] $name', 36 | error: error, 37 | stackTrace: stackTrace, 38 | ); 39 | } 40 | } 41 | 42 | @override 43 | void didUpdateProvider( 44 | ProviderBase provider, 45 | Object? previousValue, 46 | Object? newValue, 47 | ProviderContainer container, 48 | ) { 49 | final name = provider.name; 50 | if (name != null) { 51 | logger.t( 52 | '$_logPrefix[UPDATE] $name: ' 53 | 'newValue = ${newValue?.toDebugString()}', 54 | ); 55 | } 56 | } 57 | 58 | @override 59 | void didDisposeProvider( 60 | ProviderBase provider, 61 | ProviderContainer container, 62 | ) { 63 | final name = provider.name; 64 | if (name != null) { 65 | logger.t( 66 | '$_logPrefix[DISPOSE] $name', 67 | ); 68 | } 69 | } 70 | } 71 | 72 | extension _ObjectEx on Object { 73 | /// 通常の AsyncValue.toString() だとスタックトレースが変に 74 | /// 出力されるのでスタックトレースは出力しない 75 | String toDebugString() { 76 | if (this is AsyncValue) { 77 | final asyncValue = this as AsyncValue; 78 | final content = [ 79 | if (asyncValue.isLoading) 'isLoading: ${asyncValue.isLoading}', 80 | if (asyncValue.hasValue) 'value: ${asyncValue.value}', 81 | if (asyncValue.hasError) ...[ 82 | 'error: ${asyncValue.error}', 83 | ], 84 | ].join(', '); 85 | 86 | return '$runtimeType($content)'; 87 | } 88 | return toString(); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_material_color_system 2 | description: Material Design 3 のカラーシステムを確認できるサイトです。配色に悩んだときにご利用ください。 3 | publish_to: 'none' 4 | version: '3.1.0' 5 | environment: 6 | sdk: '>=3.8.1 <4.0.0' 7 | 8 | dependencies: 9 | collection: 10 | cupertino_icons: 11 | flex_color_picker: 12 | flutter: 13 | sdk: flutter 14 | flutter_riverpod: 15 | freezed_annotation: 16 | gap: 17 | hexcolor: 18 | hive: 19 | hive_flutter: 20 | material_color_utilities: 21 | package_info_plus: 22 | recase: 23 | responsive_framework: 0.2.0 24 | riverpod_annotation: 25 | roggle: 26 | shared_preferences: 27 | url_launcher: 28 | 29 | dev_dependencies: 30 | build_runner: 31 | custom_lint: 32 | flutter_gen_runner: 33 | flutter_launcher_icons: 34 | flutter_test: 35 | sdk: flutter 36 | freezed: 37 | hive_generator: 38 | pedantic_mono: 39 | riverpod_generator: 40 | riverpod_lint: 41 | 42 | flutter: 43 | uses-material-design: true 44 | assets: 45 | - assets/images/ 46 | - .dart_tool/version 47 | 48 | flutter_launcher_icons: 49 | web: 50 | generate: true 51 | image_path: 'assets/images/app-icon.png' 52 | background_color: '#ffffff' 53 | theme_color: '#6750A4' 54 | 55 | flutter_gen: 56 | output: lib/util/assets/ 57 | integrations: 58 | flutter_svg: true 59 | -------------------------------------------------------------------------------- /web/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/susatthi/flutter-material-color-system/27b4b3e9a65b2979341b0f7a7312233ce31c0644/web/favicon.ico -------------------------------------------------------------------------------- /web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/susatthi/flutter-material-color-system/27b4b3e9a65b2979341b0f7a7312233ce31c0644/web/favicon.png -------------------------------------------------------------------------------- /web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/susatthi/flutter-material-color-system/27b4b3e9a65b2979341b0f7a7312233ce31c0644/web/icons/Icon-192.png -------------------------------------------------------------------------------- /web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/susatthi/flutter-material-color-system/27b4b3e9a65b2979341b0f7a7312233ce31c0644/web/icons/Icon-512.png -------------------------------------------------------------------------------- /web/icons/Icon-maskable-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/susatthi/flutter-material-color-system/27b4b3e9a65b2979341b0f7a7312233ce31c0644/web/icons/Icon-maskable-192.png -------------------------------------------------------------------------------- /web/icons/Icon-maskable-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/susatthi/flutter-material-color-system/27b4b3e9a65b2979341b0f7a7312233ce31c0644/web/icons/Icon-maskable-512.png -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | Material Color System 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Material Color System", 3 | "short_name": "Material Color System", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#ffffff", 7 | "theme_color": "#6750A4", 8 | "description": "Material Design 3 のカラーシステムを確認できるサイトです。配色に悩んだときにご利用ください。", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | }, 22 | { 23 | "src": "icons/Icon-maskable-192.png", 24 | "sizes": "192x192", 25 | "type": "image/png", 26 | "purpose": "maskable" 27 | }, 28 | { 29 | "src": "icons/Icon-maskable-512.png", 30 | "sizes": "512x512", 31 | "type": "image/png", 32 | "purpose": "maskable" 33 | } 34 | ] 35 | } --------------------------------------------------------------------------------