├── .fvm └── fvm_config.json ├── .gitattributes ├── .github └── workflows │ └── cd.yml ├── .gitignore ├── .metadata ├── .vscode ├── launch.json └── settings.json ├── LICENSE ├── Makefile ├── README.md ├── analysis_options.yaml ├── lib ├── app.dart ├── core │ └── ui │ │ └── component │ │ ├── material.dart │ │ ├── scaffold_messenger.dart │ │ ├── scaffold_messenger.g.dart │ │ ├── theme_data.dart │ │ └── theme_data.g.dart ├── feature │ ├── home │ │ └── ui │ │ │ └── home_page.dart │ └── traffic_light │ │ ├── state │ │ ├── traffic_light_state.dart │ │ ├── traffic_light_state.freezed.dart │ │ └── traffic_light_state.g.dart │ │ ├── ui │ │ └── component │ │ │ ├── crosswalk_button.dart │ │ │ └── traffic_light.dart │ │ └── use_case │ │ ├── press_crosswalk_button.dart │ │ └── press_crosswalk_button.g.dart ├── main.dart └── util │ └── logger.dart ├── pubspec.lock ├── pubspec.yaml └── web ├── 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.16.0", 3 | "flavors": {} 4 | } -------------------------------------------------------------------------------- /.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 | --web-renderer canvaskit \ 54 | --release \ 55 | --base-href /${{ steps.version.outputs.repository }}/ 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 | # fvm related 47 | .fvm/flutter_sdk 48 | -------------------------------------------------------------------------------- /.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: "d211f42860350d914a5ad8102f9ec32764dc6d06" 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: d211f42860350d914a5ad8102f9ec32764dc6d06 17 | base_revision: d211f42860350d914a5ad8102f9ec32764dc6d06 18 | - platform: android 19 | create_revision: d211f42860350d914a5ad8102f9ec32764dc6d06 20 | base_revision: d211f42860350d914a5ad8102f9ec32764dc6d06 21 | - platform: ios 22 | create_revision: d211f42860350d914a5ad8102f9ec32764dc6d06 23 | base_revision: d211f42860350d914a5ad8102f9ec32764dc6d06 24 | - platform: linux 25 | create_revision: d211f42860350d914a5ad8102f9ec32764dc6d06 26 | base_revision: d211f42860350d914a5ad8102f9ec32764dc6d06 27 | - platform: macos 28 | create_revision: d211f42860350d914a5ad8102f9ec32764dc6d06 29 | base_revision: d211f42860350d914a5ad8102f9ec32764dc6d06 30 | - platform: web 31 | create_revision: d211f42860350d914a5ad8102f9ec32764dc6d06 32 | base_revision: d211f42860350d914a5ad8102f9ec32764dc6d06 33 | - platform: windows 34 | create_revision: d211f42860350d914a5ad8102f9ec32764dc6d06 35 | base_revision: d211f42860350d914a5ad8102f9ec32764dc6d06 36 | 37 | # User provided section 38 | 39 | # List of Local paths (relative to this file) that should be 40 | # ignored by the migrate tool. 41 | # 42 | # Files that are not part of the templates will be ignored by default. 43 | unmanaged_files: 44 | - 'lib/main.dart' 45 | - 'ios/Runner.xcodeproj/project.pbxproj' 46 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "app", 6 | "type": "dart", 7 | "request": "launch", 8 | "flutterMode": "debug", 9 | "program": "lib/main.dart", 10 | "args": [ 11 | "--web-hostname", 12 | "localhost", 13 | "--web-port", 14 | "5000", 15 | ] 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Dart Recommended Settings: https://dartcode.org/docs/recommended-settings/ 2 | { 3 | "debug.openDebug": "openOnDebugBreak", 4 | "editor.rulers": [80], 5 | "editor.renderWhitespace": "all", 6 | "editor.renderControlCharacters": true, 7 | "editor.minimap.enabled": false, 8 | "editor.bracketPairColorization.enabled": true, 9 | "[dart]": { 10 | "editor.formatOnSave": true, 11 | "editor.formatOnType": true, 12 | "editor.selectionHighlight": false, 13 | "editor.suggest.snippetsPreventQuickSuggestions": false, 14 | "editor.suggestSelection": "recentlyUsedByPrefix", 15 | "editor.tabCompletion": "onlySnippets", 16 | "editor.wordBasedSuggestions": false, 17 | "editor.codeActionsOnSave": { 18 | "source.organizeImports": "always", 19 | "source.addMissingImports": "always", 20 | "quickfix.insertSemicolon": "always", 21 | "source.fixAll": "always", 22 | } 23 | }, 24 | "[css]": { 25 | "editor.formatOnSave": true, 26 | "editor.formatOnType": true, 27 | }, 28 | "dart.flutterSdkPath": ".fvm/flutter_sdk", 29 | "dart.debugSdkLibraries": false, 30 | "dart.showSkippedTests": false, 31 | "dart.runPubGetOnPubspecChanges": "always", 32 | "search.exclude": { 33 | "**/.fvm": true, 34 | "**/*.freezed.dart": true, 35 | "**/*.g.dart": true 36 | }, 37 | "files.watcherExclude": { 38 | "**/.fvm": true 39 | }, 40 | "git.autofetch": true, 41 | "explorer.confirmDragAndDrop": false, 42 | "explorer.fileNesting.enabled": true, 43 | "explorer.fileNesting.expand": false, 44 | "explorer.fileNesting.patterns": { 45 | "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", 46 | ".env.example": ".env.*", 47 | ".gitignore": ".gitattributes, .gitmodules, .gitmessage, .mailmap, .git-blame*", 48 | "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", 49 | "*.dart": "$(capture).g.dart, $(capture).gr.dart, $(capture).freezed.dart" 50 | }, 51 | "commentTranslate.multiLineMerge": true 52 | } 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. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | FVM := $(shell which fvm) 2 | FLUTTER := $(FVM) flutter 3 | 4 | .PHONY: get-dependencies 5 | get-dependencies: 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 | 16 | .PHONY: build-runner 17 | build-runner: 18 | $(FLUTTER) packages pub run build_runner build --delete-conflicting-outputs 19 | 20 | .PHONY: build-runner-watch 21 | build-runner-watch: 22 | $(FLUTTER) packages pub run build_runner clean 23 | $(FLUTTER) packages pub run build_runner watch --delete-conflicting-outputs 24 | 25 | .PHONY: flutter-launcher-icons 26 | flutter-launcher-icons: 27 | $(FLUTTER) pub run flutter_launcher_icons:main 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 信号機シミュレーター 2 | 3 | Flutter における「状態」を解説するためのサンプル。Riverpod を利用しています。 4 | 5 | 6 | 7 | ## デモサイト 8 | 9 | https://susatthi.github.io/flutter-traffic-light/ 10 | 11 | ## License 12 | 13 | MIT 14 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/app.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 3 | 4 | import 'core/ui/component/scaffold_messenger.dart'; 5 | import 'core/ui/component/theme_data.dart'; 6 | import 'feature/home/ui/home_page.dart'; 7 | 8 | class App extends ConsumerWidget { 9 | const App({super.key}); 10 | 11 | @override 12 | Widget build(BuildContext context, WidgetRef ref) { 13 | return MaterialApp( 14 | title: 'Traffic Light', 15 | theme: ref.watch(themeDataProvider), 16 | debugShowCheckedModeBanner: false, 17 | scaffoldMessengerKey: ref.watch(scaffoldMessengerProvider), 18 | home: const HomePage(), 19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/core/ui/component/material.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | // ignore: depend_on_referenced_packages 3 | import 'package:material_color_utilities/material_color_utilities.dart'; 4 | 5 | extension BuildContextX on BuildContext { 6 | /// ダークモードかどうかを返す 7 | bool get isDark => Theme.of(this).brightness == Brightness.dark; 8 | 9 | /// カラーを返す 10 | ColorScheme get _colorScheme => Theme.of(this).colorScheme; 11 | Color get primary => _colorScheme.primary; 12 | Color get onPrimary => _colorScheme.onPrimary; 13 | Color get primaryContainer => _colorScheme.primaryContainer; 14 | Color get onPrimaryContainer => _colorScheme.onPrimaryContainer; 15 | Color get secondary => _colorScheme.secondary; 16 | Color get onSecondary => _colorScheme.onSecondary; 17 | Color get secondaryContainer => _colorScheme.secondaryContainer; 18 | Color get onSecondaryContainer => _colorScheme.onSecondaryContainer; 19 | Color get tertiary => _colorScheme.tertiary; 20 | Color get onTertiary => _colorScheme.onTertiary; 21 | Color get tertiaryContainer => _colorScheme.tertiaryContainer; 22 | Color get onTertiaryContainer => _colorScheme.onTertiaryContainer; 23 | Color get error => _colorScheme.error; 24 | Color get onError => _colorScheme.onError; 25 | Color get errorContainer => _colorScheme.errorContainer; 26 | Color get onErrorContainer => _colorScheme.onErrorContainer; 27 | Color get background => _colorScheme.background; 28 | Color get onBackground => _colorScheme.onBackground; 29 | Color get surface => _colorScheme.surface; 30 | Color get onSurface => _colorScheme.onSurface; 31 | Color get outline => _colorScheme.outline; 32 | Color get outlineVariant => _colorScheme.outlineVariant; 33 | Color get surfaceVariant => _colorScheme.surfaceVariant; 34 | Color get onSurfaceVariant => _colorScheme.onSurfaceVariant; 35 | Color get surfaceTint => _colorScheme.surfaceTint; 36 | Color get inversePrimary => _colorScheme.inversePrimary; 37 | Color get inverseSurface => _colorScheme.inverseSurface; 38 | Color get onInverseSurface => _colorScheme.onInverseSurface; 39 | 40 | /// テキストテーマを返す 41 | TextTheme get _textTheme => Theme.of(this).textTheme; 42 | TextStyle? get displayLarge => _textTheme.displayLarge; 43 | TextStyle? get displayMedium => _textTheme.displayMedium; 44 | TextStyle? get displaySmall => _textTheme.displaySmall; 45 | TextStyle? get headlineLarge => _textTheme.headlineLarge; 46 | TextStyle? get headlineMedium => _textTheme.headlineMedium; 47 | TextStyle? get headlineSmall => _textTheme.headlineSmall; 48 | TextStyle? get titleLarge => _textTheme.titleLarge; 49 | TextStyle? get titleMedium => _textTheme.titleMedium; 50 | TextStyle? get titleSmall => _textTheme.titleSmall; 51 | TextStyle? get labelLarge => _textTheme.labelLarge; 52 | TextStyle? get labelMedium => _textTheme.labelMedium; 53 | TextStyle? get labelSmall => _textTheme.labelSmall; 54 | TextStyle? get bodyLarge => _textTheme.bodyLarge; 55 | TextStyle? get bodyMedium => _textTheme.bodyMedium; 56 | TextStyle? get bodySmall => _textTheme.bodySmall; 57 | TextStyle? get caption => labelSmall?.copyWith(color: outline); 58 | } 59 | 60 | extension CorePaletteX on CorePalette { 61 | MaterialColor get primaryMaterial => primary.toMaterialColor(); 62 | MaterialColor get secondaryMaterial => secondary.toMaterialColor(); 63 | MaterialColor get tertiaryMaterial => tertiary.toMaterialColor(); 64 | MaterialColor get errorMaterial => error.toMaterialColor(); 65 | MaterialColor get neutralMaterial => neutral.toMaterialColor(); 66 | MaterialColor get neutralVariantMaterial => neutralVariant.toMaterialColor(); 67 | } 68 | 69 | extension TonalPaletteX on TonalPalette { 70 | /// MaterialColorに変換する 71 | MaterialColor toMaterialColor() { 72 | final primary = get(40); 73 | return MaterialColor(primary, { 74 | 1: Color(get(99)), 75 | 50: Color(get(95)), 76 | 100: Color(get(90)), 77 | 200: Color(get(80)), 78 | 300: Color(get(70)), 79 | 400: Color(get(60)), 80 | 500: Color(get(50)), 81 | 600: Color(primary), 82 | 700: Color(get(30)), 83 | 800: Color(get(20)), 84 | 900: Color(get(10)), 85 | }); 86 | } 87 | } 88 | 89 | extension MaterialColorX on MaterialColor { 90 | /// 標準のMaterialColorで一番明るいのはshade50だが、それよりももっと明るく 91 | /// ほぼ白色だがほんの少しだけ色がついている色 92 | Color? get shade1 => this[1]; 93 | } 94 | 95 | class MaterialSurfaceColor { 96 | const MaterialSurfaceColor._({ 97 | required this.surfaceContainerLowest, 98 | required this.surfaceContainerLow, 99 | required this.surfaceContainer, 100 | required this.surfaceContainerHigh, 101 | required this.surfaceContainerHighest, 102 | required this.surfaceDim, 103 | required this.surfaceBright, 104 | }); 105 | 106 | factory MaterialSurfaceColor.fromSeed({ 107 | required Color seedColor, 108 | Brightness brightness = Brightness.light, 109 | }) { 110 | final neutralPalette = CorePalette.of(seedColor.value).neutral; 111 | switch (brightness) { 112 | case Brightness.light: 113 | return MaterialSurfaceColor._( 114 | surfaceContainerLowest: Color(neutralPalette.get(100)), 115 | surfaceContainerLow: Color(neutralPalette.get(96)), 116 | surfaceContainer: Color(neutralPalette.get(94)), 117 | surfaceContainerHigh: Color(neutralPalette.get(92)), 118 | surfaceContainerHighest: Color(neutralPalette.get(90)), 119 | surfaceDim: Color(neutralPalette.get(87)), 120 | surfaceBright: Color(neutralPalette.get(98)), 121 | ); 122 | case Brightness.dark: 123 | return MaterialSurfaceColor._( 124 | surfaceContainerLowest: Color(neutralPalette.get(4)), 125 | surfaceContainerLow: Color(neutralPalette.get(10)), 126 | surfaceContainer: Color(neutralPalette.get(12)), 127 | surfaceContainerHigh: Color(neutralPalette.get(17)), 128 | surfaceContainerHighest: Color(neutralPalette.get(22)), 129 | surfaceDim: Color(neutralPalette.get(6)), 130 | surfaceBright: Color(neutralPalette.get(24)), 131 | ); 132 | } 133 | } 134 | 135 | final Color surfaceContainerLowest; 136 | final Color surfaceContainerLow; 137 | final Color surfaceContainer; 138 | final Color surfaceContainerHigh; 139 | final Color surfaceContainerHighest; 140 | final Color surfaceDim; 141 | final Color surfaceBright; 142 | } 143 | -------------------------------------------------------------------------------- /lib/core/ui/component/scaffold_messenger.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | 4 | part 'scaffold_messenger.g.dart'; 5 | 6 | @riverpod 7 | class ScaffoldMessenger extends _$ScaffoldMessenger { 8 | @override 9 | GlobalKey build() => 10 | GlobalKey(); 11 | 12 | ScaffoldMessengerState? get _currentState => state.currentState; 13 | 14 | ScaffoldFeatureController? showSnackBar( 15 | SnackBar snackBar, 16 | ) { 17 | return _currentState?.showSnackBar(snackBar); 18 | } 19 | 20 | void hideCurrentSnackBar({ 21 | SnackBarClosedReason reason = SnackBarClosedReason.hide, 22 | }) { 23 | _currentState?.hideCurrentSnackBar(reason: reason); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/core/ui/component/scaffold_messenger.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'scaffold_messenger.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$scaffoldMessengerHash() => r'd5314077ec9536ad8855ab048d92df2ac6ea5b05'; 10 | 11 | /// See also [ScaffoldMessenger]. 12 | @ProviderFor(ScaffoldMessenger) 13 | final scaffoldMessengerProvider = AutoDisposeNotifierProvider>.internal( 15 | ScaffoldMessenger.new, 16 | name: r'scaffoldMessengerProvider', 17 | debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') 18 | ? null 19 | : _$scaffoldMessengerHash, 20 | dependencies: null, 21 | allTransitiveDependencies: null, 22 | ); 23 | 24 | typedef _$ScaffoldMessenger 25 | = 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 28 | -------------------------------------------------------------------------------- /lib/core/ui/component/theme_data.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | 4 | part 'theme_data.g.dart'; 5 | 6 | @riverpod 7 | Color seedColor(SeedColorRef ref) { 8 | return Colors.green; 9 | } 10 | 11 | @riverpod 12 | ThemeData themeData(ThemeDataRef ref) { 13 | final seedColor = ref.watch(seedColorProvider); 14 | return ThemeData( 15 | useMaterial3: true, 16 | colorSchemeSeed: seedColor, 17 | brightness: Brightness.light, 18 | snackBarTheme: const SnackBarThemeData( 19 | behavior: SnackBarBehavior.floating, 20 | ), 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /lib/core/ui/component/theme_data.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'theme_data.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$seedColorHash() => r'd45536b6a339adf3f49f4f27fddf7444b5931042'; 10 | 11 | /// See also [seedColor]. 12 | @ProviderFor(seedColor) 13 | final seedColorProvider = AutoDisposeProvider.internal( 14 | seedColor, 15 | name: r'seedColorProvider', 16 | debugGetCreateSourceHash: 17 | const bool.fromEnvironment('dart.vm.product') ? null : _$seedColorHash, 18 | dependencies: null, 19 | allTransitiveDependencies: null, 20 | ); 21 | 22 | typedef SeedColorRef = AutoDisposeProviderRef; 23 | String _$themeDataHash() => r'302a5b779d7220a0a5ea204d1e4f137db767fa45'; 24 | 25 | /// See also [themeData]. 26 | @ProviderFor(themeData) 27 | final themeDataProvider = AutoDisposeProvider.internal( 28 | themeData, 29 | name: r'themeDataProvider', 30 | debugGetCreateSourceHash: 31 | const bool.fromEnvironment('dart.vm.product') ? null : _$themeDataHash, 32 | dependencies: null, 33 | allTransitiveDependencies: null, 34 | ); 35 | 36 | typedef ThemeDataRef = AutoDisposeProviderRef; 37 | // ignore_for_file: type=lint 38 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member 39 | -------------------------------------------------------------------------------- /lib/feature/home/ui/home_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:gap/gap.dart'; 3 | 4 | import '../../traffic_light/ui/component/crosswalk_button.dart'; 5 | import '../../traffic_light/ui/component/traffic_light.dart'; 6 | 7 | class HomePage extends StatelessWidget { 8 | const HomePage({super.key}); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return const Scaffold( 13 | body: _Body(), 14 | ); 15 | } 16 | } 17 | 18 | class _Body extends StatelessWidget { 19 | const _Body(); 20 | 21 | @override 22 | Widget build(BuildContext context) { 23 | return const SingleChildScrollView( 24 | child: Column( 25 | mainAxisAlignment: MainAxisAlignment.center, 26 | children: [ 27 | Gap(32), 28 | TrafficLight(), 29 | Gap(32), 30 | CrosswalkButton(), 31 | Gap(32), 32 | ], 33 | ), 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/feature/traffic_light/state/traffic_light_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | 4 | part 'traffic_light_state.freezed.dart'; 5 | part 'traffic_light_state.g.dart'; 6 | 7 | @freezed 8 | class TrafficLightState with _$TrafficLightState { 9 | const factory TrafficLightState({ 10 | /// 現在の信号機の状態 11 | required TrafficLightStatus status, 12 | 13 | /// 現在の信号機の状態の残り時間 14 | required int remainingTime, 15 | }) = _TrafficLightState; 16 | } 17 | 18 | enum TrafficLightStatus { 19 | red, 20 | yellow, 21 | green, 22 | ; 23 | 24 | /// 次の信号機の状態を返す。 25 | TrafficLightStatus get next => switch (this) { 26 | TrafficLightStatus.red => TrafficLightStatus.green, 27 | TrafficLightStatus.yellow => TrafficLightStatus.red, 28 | TrafficLightStatus.green => TrafficLightStatus.yellow, 29 | }; 30 | } 31 | 32 | @riverpod 33 | class TrafficLightStateNotifier extends _$TrafficLightStateNotifier { 34 | @override 35 | TrafficLightState build() { 36 | const status = TrafficLightStatus.green; 37 | return TrafficLightState( 38 | status: status, 39 | remainingTime: _getStayingTime(status), 40 | ); 41 | } 42 | 43 | /// 時間を進める。 44 | void tick() { 45 | final remainingTime = state.remainingTime - 1; 46 | if (remainingTime <= 0) { 47 | final nextStatus = state.status.next; 48 | state = state.copyWith( 49 | status: nextStatus, 50 | remainingTime: _getStayingTime(nextStatus), 51 | ); 52 | } else { 53 | state = state.copyWith( 54 | remainingTime: remainingTime, 55 | ); 56 | } 57 | } 58 | 59 | /// 信号機の状態ごとの滞在時間を返す。 60 | static int _getStayingTime(TrafficLightStatus status) => switch (status) { 61 | TrafficLightStatus.red => 10, 62 | TrafficLightStatus.yellow => 3, 63 | TrafficLightStatus.green => 8, 64 | }; 65 | 66 | /// 強制的に信号機の状態を変える。 67 | Future updateStatus(TrafficLightStatus nextStatus) async { 68 | // 非同期処理にしたいので1秒待つ。 69 | await Future.delayed(const Duration(seconds: 1)); 70 | 71 | // 赤信号 => 青信号 72 | if (state.status == TrafficLightStatus.red && 73 | nextStatus == TrafficLightStatus.green) { 74 | state = state.copyWith( 75 | status: nextStatus, 76 | remainingTime: _getStayingTime(nextStatus), 77 | ); 78 | return; 79 | } 80 | 81 | // 上記以外であればエラーを返す。 82 | throw Exception(); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /lib/feature/traffic_light/state/traffic_light_state.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 'traffic_light_state.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#custom-getters-and-methods'); 16 | 17 | /// @nodoc 18 | mixin _$TrafficLightState { 19 | /// 現在の信号機の状態 20 | TrafficLightStatus get status => throw _privateConstructorUsedError; 21 | 22 | /// 現在の信号機の状態の残り時間 23 | int get remainingTime => throw _privateConstructorUsedError; 24 | 25 | @JsonKey(ignore: true) 26 | $TrafficLightStateCopyWith get copyWith => 27 | throw _privateConstructorUsedError; 28 | } 29 | 30 | /// @nodoc 31 | abstract class $TrafficLightStateCopyWith<$Res> { 32 | factory $TrafficLightStateCopyWith( 33 | TrafficLightState value, $Res Function(TrafficLightState) then) = 34 | _$TrafficLightStateCopyWithImpl<$Res, TrafficLightState>; 35 | @useResult 36 | $Res call({TrafficLightStatus status, int remainingTime}); 37 | } 38 | 39 | /// @nodoc 40 | class _$TrafficLightStateCopyWithImpl<$Res, $Val extends TrafficLightState> 41 | implements $TrafficLightStateCopyWith<$Res> { 42 | _$TrafficLightStateCopyWithImpl(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 | @pragma('vm:prefer-inline') 50 | @override 51 | $Res call({ 52 | Object? status = null, 53 | Object? remainingTime = null, 54 | }) { 55 | return _then(_value.copyWith( 56 | status: null == status 57 | ? _value.status 58 | : status // ignore: cast_nullable_to_non_nullable 59 | as TrafficLightStatus, 60 | remainingTime: null == remainingTime 61 | ? _value.remainingTime 62 | : remainingTime // ignore: cast_nullable_to_non_nullable 63 | as int, 64 | ) as $Val); 65 | } 66 | } 67 | 68 | /// @nodoc 69 | abstract class _$$TrafficLightStateImplCopyWith<$Res> 70 | implements $TrafficLightStateCopyWith<$Res> { 71 | factory _$$TrafficLightStateImplCopyWith(_$TrafficLightStateImpl value, 72 | $Res Function(_$TrafficLightStateImpl) then) = 73 | __$$TrafficLightStateImplCopyWithImpl<$Res>; 74 | @override 75 | @useResult 76 | $Res call({TrafficLightStatus status, int remainingTime}); 77 | } 78 | 79 | /// @nodoc 80 | class __$$TrafficLightStateImplCopyWithImpl<$Res> 81 | extends _$TrafficLightStateCopyWithImpl<$Res, _$TrafficLightStateImpl> 82 | implements _$$TrafficLightStateImplCopyWith<$Res> { 83 | __$$TrafficLightStateImplCopyWithImpl(_$TrafficLightStateImpl _value, 84 | $Res Function(_$TrafficLightStateImpl) _then) 85 | : super(_value, _then); 86 | 87 | @pragma('vm:prefer-inline') 88 | @override 89 | $Res call({ 90 | Object? status = null, 91 | Object? remainingTime = null, 92 | }) { 93 | return _then(_$TrafficLightStateImpl( 94 | status: null == status 95 | ? _value.status 96 | : status // ignore: cast_nullable_to_non_nullable 97 | as TrafficLightStatus, 98 | remainingTime: null == remainingTime 99 | ? _value.remainingTime 100 | : remainingTime // ignore: cast_nullable_to_non_nullable 101 | as int, 102 | )); 103 | } 104 | } 105 | 106 | /// @nodoc 107 | 108 | class _$TrafficLightStateImpl implements _TrafficLightState { 109 | const _$TrafficLightStateImpl( 110 | {required this.status, required this.remainingTime}); 111 | 112 | /// 現在の信号機の状態 113 | @override 114 | final TrafficLightStatus status; 115 | 116 | /// 現在の信号機の状態の残り時間 117 | @override 118 | final int remainingTime; 119 | 120 | @override 121 | String toString() { 122 | return 'TrafficLightState(status: $status, remainingTime: $remainingTime)'; 123 | } 124 | 125 | @override 126 | bool operator ==(dynamic other) { 127 | return identical(this, other) || 128 | (other.runtimeType == runtimeType && 129 | other is _$TrafficLightStateImpl && 130 | (identical(other.status, status) || other.status == status) && 131 | (identical(other.remainingTime, remainingTime) || 132 | other.remainingTime == remainingTime)); 133 | } 134 | 135 | @override 136 | int get hashCode => Object.hash(runtimeType, status, remainingTime); 137 | 138 | @JsonKey(ignore: true) 139 | @override 140 | @pragma('vm:prefer-inline') 141 | _$$TrafficLightStateImplCopyWith<_$TrafficLightStateImpl> get copyWith => 142 | __$$TrafficLightStateImplCopyWithImpl<_$TrafficLightStateImpl>( 143 | this, _$identity); 144 | } 145 | 146 | abstract class _TrafficLightState implements TrafficLightState { 147 | const factory _TrafficLightState( 148 | {required final TrafficLightStatus status, 149 | required final int remainingTime}) = _$TrafficLightStateImpl; 150 | 151 | @override 152 | 153 | /// 現在の信号機の状態 154 | TrafficLightStatus get status; 155 | @override 156 | 157 | /// 現在の信号機の状態の残り時間 158 | int get remainingTime; 159 | @override 160 | @JsonKey(ignore: true) 161 | _$$TrafficLightStateImplCopyWith<_$TrafficLightStateImpl> get copyWith => 162 | throw _privateConstructorUsedError; 163 | } 164 | -------------------------------------------------------------------------------- /lib/feature/traffic_light/state/traffic_light_state.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'traffic_light_state.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$trafficLightStateNotifierHash() => 10 | r'aba3071fbb8d91a1e34c16a8c6730211c5835e4e'; 11 | 12 | /// See also [TrafficLightStateNotifier]. 13 | @ProviderFor(TrafficLightStateNotifier) 14 | final trafficLightStateNotifierProvider = AutoDisposeNotifierProvider< 15 | TrafficLightStateNotifier, TrafficLightState>.internal( 16 | TrafficLightStateNotifier.new, 17 | name: r'trafficLightStateNotifierProvider', 18 | debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') 19 | ? null 20 | : _$trafficLightStateNotifierHash, 21 | dependencies: null, 22 | allTransitiveDependencies: null, 23 | ); 24 | 25 | typedef _$TrafficLightStateNotifier = 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 28 | -------------------------------------------------------------------------------- /lib/feature/traffic_light/ui/component/crosswalk_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/material.dart'; 6 | import '../../../../core/ui/component/scaffold_messenger.dart'; 7 | import '../../use_case/press_crosswalk_button.dart'; 8 | 9 | class CrosswalkButton extends StatelessWidget { 10 | const CrosswalkButton({super.key}); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return ColoredBox( 15 | color: const Color(0xFFFFD444), 16 | child: Padding( 17 | padding: const EdgeInsets.all(16), 18 | child: Column( 19 | children: [ 20 | Text( 21 | 'おしてください', 22 | style: context.headlineMedium?.copyWith( 23 | fontWeight: FontWeight.bold, 24 | ), 25 | ), 26 | const Gap(32), 27 | const _Button(), 28 | const Gap(16), 29 | Text( 30 | '横断歩行者は\nボタンをおしてください', 31 | style: context.titleLarge, 32 | textAlign: TextAlign.center, 33 | ), 34 | ], 35 | ), 36 | ), 37 | ); 38 | } 39 | } 40 | 41 | class _Button extends ConsumerWidget { 42 | const _Button(); 43 | 44 | @override 45 | Widget build(BuildContext context, WidgetRef ref) { 46 | ref.listen(pressCrosswalkButtonUseCaseProvider, (previous, next) { 47 | next.when( 48 | data: (_) {}, 49 | error: (e, s) { 50 | ref.read(scaffoldMessengerProvider).currentState?.showSnackBar( 51 | const SnackBar( 52 | content: Text('青にできませんでした。'), 53 | duration: Duration(seconds: 2), 54 | width: 300, 55 | ), 56 | ); 57 | }, 58 | loading: () {}, 59 | ); 60 | }); 61 | final isLoading = ref.watch(pressCrosswalkButtonUseCaseProvider).isLoading; 62 | return SizedBox.square( 63 | dimension: 80, 64 | child: IconButton( 65 | onPressed: isLoading 66 | ? null 67 | : () => 68 | ref.read(pressCrosswalkButtonUseCaseProvider.notifier).invoke(), 69 | style: IconButton.styleFrom( 70 | foregroundColor: context.onError, 71 | backgroundColor: context.error, 72 | disabledBackgroundColor: context.onSurface.withOpacity(0.12), 73 | hoverColor: context.onError.withOpacity(0.08), 74 | focusColor: context.onError.withOpacity(0.12), 75 | highlightColor: context.onError.withOpacity(0.12), 76 | ), 77 | icon: const Icon( 78 | null, 79 | color: Colors.transparent, 80 | ), 81 | ), 82 | ); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /lib/feature/traffic_light/ui/component/traffic_light.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 5 | import 'package:gap/gap.dart'; 6 | 7 | import '../../state/traffic_light_state.dart'; 8 | 9 | class TrafficLight extends StatelessWidget { 10 | const TrafficLight({super.key}); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return TrafficLightBuilder( 15 | builder: (state) { 16 | return Row( 17 | mainAxisAlignment: MainAxisAlignment.center, 18 | crossAxisAlignment: CrossAxisAlignment.end, 19 | children: [ 20 | SizedBox( 21 | height: 360, 22 | width: 180, 23 | child: _TrafficLight( 24 | state: state, 25 | iconSize: 150, 26 | ), 27 | ), 28 | const Gap(8), 29 | SizedBox( 30 | width: 80, 31 | height: 180, 32 | child: _RemainingSignage( 33 | // 黄色の場合は残り時間を表示しない。 34 | remainingTime: state.status == TrafficLightStatus.yellow 35 | ? 0 36 | : state.remainingTime, 37 | ), 38 | ), 39 | ], 40 | ); 41 | }, 42 | ); 43 | } 44 | } 45 | 46 | class _TrafficLight extends StatelessWidget { 47 | const _TrafficLight({ 48 | required this.state, 49 | required this.iconSize, 50 | }); 51 | 52 | final TrafficLightState state; 53 | final double iconSize; 54 | 55 | @override 56 | Widget build(BuildContext context) { 57 | return switch (state.status) { 58 | TrafficLightStatus.red => Column( 59 | children: [ 60 | _RedLightOn( 61 | iconSize: iconSize, 62 | ), 63 | _GreenLightOff( 64 | iconSize: iconSize, 65 | ), 66 | ], 67 | ), 68 | TrafficLightStatus.yellow => Column( 69 | children: [ 70 | _RedLightOff( 71 | iconSize: iconSize, 72 | ), 73 | _GreenLightBlinking( 74 | iconSize: iconSize, 75 | ), 76 | ], 77 | ), 78 | TrafficLightStatus.green => Column( 79 | children: [ 80 | _RedLightOff( 81 | iconSize: iconSize, 82 | ), 83 | _GreenLightOn( 84 | iconSize: iconSize, 85 | ), 86 | ], 87 | ), 88 | }; 89 | } 90 | } 91 | 92 | const _redOnBackgroundColor = Color(0xFFEA5350); 93 | const _greenOnBackgroundColor = Color(0xFF62C0B5); 94 | const _offBackgroundColor = Color(0xFF3B2B29); 95 | const _onForegroundColor = Color(0xFFFEE799); 96 | const _offForegroundColor = Color(0xFF5E504F); 97 | 98 | class _RedLightOn extends StatelessWidget { 99 | const _RedLightOn({ 100 | required this.iconSize, 101 | }); 102 | final double iconSize; 103 | 104 | @override 105 | Widget build(BuildContext context) { 106 | return _Light( 107 | icon: Icons.accessibility, 108 | iconSize: iconSize, 109 | backgroundColor: _redOnBackgroundColor, 110 | foregroundColor: _onForegroundColor, 111 | ); 112 | } 113 | } 114 | 115 | class _RedLightOff extends StatelessWidget { 116 | const _RedLightOff({ 117 | required this.iconSize, 118 | }); 119 | final double iconSize; 120 | 121 | @override 122 | Widget build(BuildContext context) { 123 | return _Light( 124 | icon: Icons.accessibility, 125 | iconSize: iconSize, 126 | backgroundColor: _offBackgroundColor, 127 | foregroundColor: _offForegroundColor, 128 | ); 129 | } 130 | } 131 | 132 | class _GreenLightBlinking extends StatefulWidget { 133 | const _GreenLightBlinking({ 134 | required this.iconSize, 135 | }); 136 | final double iconSize; 137 | 138 | @override 139 | State<_GreenLightBlinking> createState() => _GreenLightBlinkingState(); 140 | } 141 | 142 | class _GreenLightBlinkingState extends State<_GreenLightBlinking> { 143 | late final Timer timer; 144 | bool _isOn = false; 145 | 146 | @override 147 | void initState() { 148 | super.initState(); 149 | timer = Timer.periodic(const Duration(milliseconds: 300), (timer) { 150 | setState(() { 151 | _isOn = !_isOn; 152 | }); 153 | }); 154 | } 155 | 156 | @override 157 | void dispose() { 158 | timer.cancel(); 159 | super.dispose(); 160 | } 161 | 162 | @override 163 | Widget build(BuildContext context) { 164 | return _Light( 165 | icon: Icons.directions_walk, 166 | iconSize: widget.iconSize, 167 | backgroundColor: _greenOnBackgroundColor, 168 | foregroundColor: _isOn ? _onForegroundColor : _offForegroundColor, 169 | ); 170 | } 171 | } 172 | 173 | class _GreenLightOn extends StatelessWidget { 174 | const _GreenLightOn({ 175 | required this.iconSize, 176 | }); 177 | final double iconSize; 178 | 179 | @override 180 | Widget build(BuildContext context) { 181 | return _Light( 182 | icon: Icons.directions_walk, 183 | iconSize: iconSize, 184 | backgroundColor: _greenOnBackgroundColor, 185 | foregroundColor: _onForegroundColor, 186 | ); 187 | } 188 | } 189 | 190 | class _GreenLightOff extends StatelessWidget { 191 | const _GreenLightOff({ 192 | required this.iconSize, 193 | }); 194 | final double iconSize; 195 | 196 | @override 197 | Widget build(BuildContext context) { 198 | return _Light( 199 | icon: Icons.directions_walk, 200 | iconSize: iconSize, 201 | backgroundColor: _offBackgroundColor, 202 | foregroundColor: _offForegroundColor, 203 | ); 204 | } 205 | } 206 | 207 | class _Light extends StatelessWidget { 208 | const _Light({ 209 | required this.icon, 210 | required this.iconSize, 211 | required this.backgroundColor, 212 | required this.foregroundColor, 213 | }); 214 | 215 | final IconData icon; 216 | final double iconSize; 217 | final Color backgroundColor; 218 | final Color foregroundColor; 219 | 220 | @override 221 | Widget build(BuildContext context) { 222 | return Expanded( 223 | child: SizedBox( 224 | width: double.infinity, 225 | child: ColoredBox( 226 | color: backgroundColor, 227 | child: Icon( 228 | icon, 229 | color: foregroundColor, 230 | size: iconSize, 231 | ), 232 | ), 233 | ), 234 | ); 235 | } 236 | } 237 | 238 | class _RemainingSignage extends StatelessWidget { 239 | const _RemainingSignage({ 240 | required this.remainingTime, 241 | }); 242 | 243 | final int remainingTime; 244 | 245 | static const _maxBarCount = 10; 246 | 247 | @override 248 | Widget build(BuildContext context) { 249 | return ColoredBox( 250 | color: _offForegroundColor, 251 | child: Padding( 252 | padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 8), 253 | child: Column( 254 | children: [ 255 | for (var i = _maxBarCount + 1; i > 1; i--) 256 | Expanded( 257 | child: _RemainingBar( 258 | isOn: i <= remainingTime, 259 | ), 260 | ), 261 | ], 262 | ), 263 | ), 264 | ); 265 | } 266 | } 267 | 268 | class _RemainingBar extends StatelessWidget { 269 | const _RemainingBar({ 270 | required this.isOn, 271 | }); 272 | 273 | final bool isOn; 274 | 275 | @override 276 | Widget build(BuildContext context) { 277 | return Padding( 278 | padding: const EdgeInsets.symmetric(vertical: 4), 279 | child: SizedBox( 280 | width: double.infinity, 281 | child: ColoredBox( 282 | color: isOn ? _onForegroundColor : _offForegroundColor, 283 | ), 284 | ), 285 | ); 286 | } 287 | } 288 | 289 | class TrafficLightBuilder extends ConsumerStatefulWidget { 290 | const TrafficLightBuilder({ 291 | super.key, 292 | required this.builder, 293 | }); 294 | 295 | final Widget Function(TrafficLightState state) builder; 296 | 297 | @override 298 | ConsumerState createState() => 299 | _TrafficLightBuilderState(); 300 | } 301 | 302 | class _TrafficLightBuilderState extends ConsumerState { 303 | late final Timer timer; 304 | 305 | @override 306 | void initState() { 307 | super.initState(); 308 | timer = Timer.periodic(const Duration(seconds: 1), (timer) { 309 | ref.read(trafficLightStateNotifierProvider.notifier).tick(); 310 | }); 311 | } 312 | 313 | @override 314 | void dispose() { 315 | timer.cancel(); 316 | super.dispose(); 317 | } 318 | 319 | @override 320 | Widget build(BuildContext context) { 321 | final state = ref.watch(trafficLightStateNotifierProvider); 322 | return widget.builder(state); 323 | } 324 | } 325 | -------------------------------------------------------------------------------- /lib/feature/traffic_light/use_case/press_crosswalk_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 2 | 3 | import '../state/traffic_light_state.dart'; 4 | 5 | part 'press_crosswalk_button.g.dart'; 6 | 7 | @riverpod 8 | class PressCrosswalkButtonUseCase extends _$PressCrosswalkButtonUseCase { 9 | @override 10 | FutureOr build() { 11 | return null; 12 | } 13 | 14 | Future invoke() async { 15 | if (state.isLoading) { 16 | return; 17 | } 18 | 19 | state = const AsyncValue.loading(); 20 | state = await AsyncValue.guard(() async { 21 | await ref 22 | .read(trafficLightStateNotifierProvider.notifier) 23 | .updateStatus(TrafficLightStatus.green); 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/feature/traffic_light/use_case/press_crosswalk_button.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'press_crosswalk_button.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$pressCrosswalkButtonUseCaseHash() => 10 | r'5f2d94a9e9b28daa38ed8268d6e88dc767cdd24d'; 11 | 12 | /// See also [PressCrosswalkButtonUseCase]. 13 | @ProviderFor(PressCrosswalkButtonUseCase) 14 | final pressCrosswalkButtonUseCaseProvider = AutoDisposeAsyncNotifierProvider< 15 | PressCrosswalkButtonUseCase, void>.internal( 16 | PressCrosswalkButtonUseCase.new, 17 | name: r'pressCrosswalkButtonUseCaseProvider', 18 | debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') 19 | ? null 20 | : _$pressCrosswalkButtonUseCaseHash, 21 | dependencies: null, 22 | allTransitiveDependencies: null, 23 | ); 24 | 25 | typedef _$PressCrosswalkButtonUseCase = 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 28 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 3 | // ignore: depend_on_referenced_packages 4 | import 'package:flutter_web_plugins/url_strategy.dart'; 5 | 6 | import 'app.dart'; 7 | 8 | Future main() async { 9 | WidgetsFlutterBinding.ensureInitialized(); 10 | usePathUrlStrategy(); 11 | runApp( 12 | const ProviderScope( 13 | child: App(), 14 | ), 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | _fe_analyzer_shared: 5 | dependency: transitive 6 | description: 7 | name: _fe_analyzer_shared 8 | sha256: eb376e9acf6938204f90eb3b1f00b578640d3188b4c8a8ec054f9f479af8d051 9 | url: "https://pub.dev" 10 | source: hosted 11 | version: "64.0.0" 12 | analyzer: 13 | dependency: transitive 14 | description: 15 | name: analyzer 16 | sha256: "69f54f967773f6c26c7dcb13e93d7ccee8b17a641689da39e878d5cf13b06893" 17 | url: "https://pub.dev" 18 | source: hosted 19 | version: "6.2.0" 20 | analyzer_plugin: 21 | dependency: transitive 22 | description: 23 | name: analyzer_plugin 24 | sha256: "9661b30b13a685efaee9f02e5d01ed9f2b423bd889d28a304d02d704aee69161" 25 | url: "https://pub.dev" 26 | source: hosted 27 | version: "0.11.3" 28 | args: 29 | dependency: transitive 30 | description: 31 | name: args 32 | sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 33 | url: "https://pub.dev" 34 | source: hosted 35 | version: "2.4.2" 36 | async: 37 | dependency: transitive 38 | description: 39 | name: async 40 | sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" 41 | url: "https://pub.dev" 42 | source: hosted 43 | version: "2.11.0" 44 | boolean_selector: 45 | dependency: transitive 46 | description: 47 | name: boolean_selector 48 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" 49 | url: "https://pub.dev" 50 | source: hosted 51 | version: "2.1.1" 52 | build: 53 | dependency: transitive 54 | description: 55 | name: build 56 | sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" 57 | url: "https://pub.dev" 58 | source: hosted 59 | version: "2.4.1" 60 | build_config: 61 | dependency: transitive 62 | description: 63 | name: build_config 64 | sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 65 | url: "https://pub.dev" 66 | source: hosted 67 | version: "1.1.1" 68 | build_daemon: 69 | dependency: transitive 70 | description: 71 | name: build_daemon 72 | sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1" 73 | url: "https://pub.dev" 74 | source: hosted 75 | version: "4.0.1" 76 | build_resolvers: 77 | dependency: transitive 78 | description: 79 | name: build_resolvers 80 | sha256: "64e12b0521812d1684b1917bc80945625391cb9bdd4312536b1d69dcb6133ed8" 81 | url: "https://pub.dev" 82 | source: hosted 83 | version: "2.4.1" 84 | build_runner: 85 | dependency: "direct dev" 86 | description: 87 | name: build_runner 88 | sha256: "10c6bcdbf9d049a0b666702cf1cee4ddfdc38f02a19d35ae392863b47519848b" 89 | url: "https://pub.dev" 90 | source: hosted 91 | version: "2.4.6" 92 | build_runner_core: 93 | dependency: transitive 94 | description: 95 | name: build_runner_core 96 | sha256: c9e32d21dd6626b5c163d48b037ce906bbe428bc23ab77bcd77bb21e593b6185 97 | url: "https://pub.dev" 98 | source: hosted 99 | version: "7.2.11" 100 | built_collection: 101 | dependency: transitive 102 | description: 103 | name: built_collection 104 | sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" 105 | url: "https://pub.dev" 106 | source: hosted 107 | version: "5.1.1" 108 | built_value: 109 | dependency: transitive 110 | description: 111 | name: built_value 112 | sha256: "723b4021e903217dfc445ec4cf5b42e27975aece1fc4ebbc1ca6329c2d9fb54e" 113 | url: "https://pub.dev" 114 | source: hosted 115 | version: "8.7.0" 116 | characters: 117 | dependency: transitive 118 | description: 119 | name: characters 120 | sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" 121 | url: "https://pub.dev" 122 | source: hosted 123 | version: "1.3.0" 124 | checked_yaml: 125 | dependency: transitive 126 | description: 127 | name: checked_yaml 128 | sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff 129 | url: "https://pub.dev" 130 | source: hosted 131 | version: "2.0.3" 132 | ci: 133 | dependency: transitive 134 | description: 135 | name: ci 136 | sha256: "145d095ce05cddac4d797a158bc4cf3b6016d1fe63d8c3d2fbd7212590adca13" 137 | url: "https://pub.dev" 138 | source: hosted 139 | version: "0.1.0" 140 | cli_util: 141 | dependency: transitive 142 | description: 143 | name: cli_util 144 | sha256: b8db3080e59b2503ca9e7922c3df2072cf13992354d5e944074ffa836fba43b7 145 | url: "https://pub.dev" 146 | source: hosted 147 | version: "0.4.0" 148 | clock: 149 | dependency: transitive 150 | description: 151 | name: clock 152 | sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf 153 | url: "https://pub.dev" 154 | source: hosted 155 | version: "1.1.1" 156 | code_builder: 157 | dependency: transitive 158 | description: 159 | name: code_builder 160 | sha256: b2151ce26a06171005b379ecff6e08d34c470180ffe16b8e14b6d52be292b55f 161 | url: "https://pub.dev" 162 | source: hosted 163 | version: "4.8.0" 164 | collection: 165 | dependency: "direct main" 166 | description: 167 | name: collection 168 | sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a 169 | url: "https://pub.dev" 170 | source: hosted 171 | version: "1.18.0" 172 | color: 173 | dependency: transitive 174 | description: 175 | name: color 176 | sha256: ddcdf1b3badd7008233f5acffaf20ca9f5dc2cd0172b75f68f24526a5f5725cb 177 | url: "https://pub.dev" 178 | source: hosted 179 | version: "3.0.0" 180 | convert: 181 | dependency: transitive 182 | description: 183 | name: convert 184 | sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" 185 | url: "https://pub.dev" 186 | source: hosted 187 | version: "3.1.1" 188 | crypto: 189 | dependency: transitive 190 | description: 191 | name: crypto 192 | sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab 193 | url: "https://pub.dev" 194 | source: hosted 195 | version: "3.0.3" 196 | cupertino_icons: 197 | dependency: "direct main" 198 | description: 199 | name: cupertino_icons 200 | sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d 201 | url: "https://pub.dev" 202 | source: hosted 203 | version: "1.0.6" 204 | custom_lint: 205 | dependency: "direct dev" 206 | description: 207 | name: custom_lint 208 | sha256: "198ec6b8e084d22f508a76556c9afcfb71706ad3f42b083fe0ee923351a96d90" 209 | url: "https://pub.dev" 210 | source: hosted 211 | version: "0.5.7" 212 | custom_lint_builder: 213 | dependency: transitive 214 | description: 215 | name: custom_lint_builder 216 | sha256: dfcfa987d2bd9d0ba751ef4bdef0f6c1aa0062f2a67fe716fd5f3f8b709d6418 217 | url: "https://pub.dev" 218 | source: hosted 219 | version: "0.5.7" 220 | custom_lint_core: 221 | dependency: transitive 222 | description: 223 | name: custom_lint_core 224 | sha256: f84c3fe2f27ef3b8831953e477e59d4a29c2952623f9eac450d7b40d9cdd94cc 225 | url: "https://pub.dev" 226 | source: hosted 227 | version: "0.5.7" 228 | dart_style: 229 | dependency: transitive 230 | description: 231 | name: dart_style 232 | sha256: "40ae61a5d43feea6d24bd22c0537a6629db858963b99b4bc1c3db80676f32368" 233 | url: "https://pub.dev" 234 | source: hosted 235 | version: "2.3.4" 236 | dartx: 237 | dependency: transitive 238 | description: 239 | name: dartx 240 | sha256: "8b25435617027257d43e6508b5fe061012880ddfdaa75a71d607c3de2a13d244" 241 | url: "https://pub.dev" 242 | source: hosted 243 | version: "1.2.0" 244 | fake_async: 245 | dependency: transitive 246 | description: 247 | name: fake_async 248 | sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" 249 | url: "https://pub.dev" 250 | source: hosted 251 | version: "1.3.1" 252 | file: 253 | dependency: transitive 254 | description: 255 | name: file 256 | sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" 257 | url: "https://pub.dev" 258 | source: hosted 259 | version: "7.0.0" 260 | fixnum: 261 | dependency: transitive 262 | description: 263 | name: fixnum 264 | sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" 265 | url: "https://pub.dev" 266 | source: hosted 267 | version: "1.1.0" 268 | flutter: 269 | dependency: "direct main" 270 | description: flutter 271 | source: sdk 272 | version: "0.0.0" 273 | flutter_gen_core: 274 | dependency: transitive 275 | description: 276 | name: flutter_gen_core 277 | sha256: "8b4ff1d45d125e576e26ea99d15e0419bb3c45b53696e022880866b78bb6b830" 278 | url: "https://pub.dev" 279 | source: hosted 280 | version: "5.3.2" 281 | flutter_gen_runner: 282 | dependency: "direct dev" 283 | description: 284 | name: flutter_gen_runner 285 | sha256: fd197f8c657e79313d53d3934de602ebe604ba722a84c88ae3a43cd90428c67a 286 | url: "https://pub.dev" 287 | source: hosted 288 | version: "5.3.2" 289 | flutter_lints: 290 | dependency: transitive 291 | description: 292 | name: flutter_lints 293 | sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 294 | url: "https://pub.dev" 295 | source: hosted 296 | version: "2.0.3" 297 | flutter_riverpod: 298 | dependency: "direct main" 299 | description: 300 | name: flutter_riverpod 301 | sha256: d261b0f2461e0595b96f92ed807841eb72cea84a6b12b8fd0c76e5ed803e7921 302 | url: "https://pub.dev" 303 | source: hosted 304 | version: "2.4.8" 305 | flutter_test: 306 | dependency: "direct dev" 307 | description: flutter 308 | source: sdk 309 | version: "0.0.0" 310 | flutter_web_plugins: 311 | dependency: "direct main" 312 | description: flutter 313 | source: sdk 314 | version: "0.0.0" 315 | freezed: 316 | dependency: "direct dev" 317 | description: 318 | name: freezed 319 | sha256: "21bf2825311de65501d22e563e3d7605dff57fb5e6da982db785ae5372ff018a" 320 | url: "https://pub.dev" 321 | source: hosted 322 | version: "2.4.5" 323 | freezed_annotation: 324 | dependency: "direct main" 325 | description: 326 | name: freezed_annotation 327 | sha256: c3fd9336eb55a38cc1bbd79ab17573113a8deccd0ecbbf926cca3c62803b5c2d 328 | url: "https://pub.dev" 329 | source: hosted 330 | version: "2.4.1" 331 | frontend_server_client: 332 | dependency: transitive 333 | description: 334 | name: frontend_server_client 335 | sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" 336 | url: "https://pub.dev" 337 | source: hosted 338 | version: "3.2.0" 339 | gap: 340 | dependency: "direct main" 341 | description: 342 | name: gap 343 | sha256: f19387d4e32f849394758b91377f9153a1b41d79513ef7668c088c77dbc6955d 344 | url: "https://pub.dev" 345 | source: hosted 346 | version: "3.0.1" 347 | glob: 348 | dependency: transitive 349 | description: 350 | name: glob 351 | sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" 352 | url: "https://pub.dev" 353 | source: hosted 354 | version: "2.1.2" 355 | graphs: 356 | dependency: transitive 357 | description: 358 | name: graphs 359 | sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19 360 | url: "https://pub.dev" 361 | source: hosted 362 | version: "2.3.1" 363 | hotreloader: 364 | dependency: transitive 365 | description: 366 | name: hotreloader 367 | sha256: "94ee21a60ea2836500799f3af035dc3212b1562027f1e0031c14e087f0231449" 368 | url: "https://pub.dev" 369 | source: hosted 370 | version: "4.1.0" 371 | http_multi_server: 372 | dependency: transitive 373 | description: 374 | name: http_multi_server 375 | sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" 376 | url: "https://pub.dev" 377 | source: hosted 378 | version: "3.2.1" 379 | http_parser: 380 | dependency: transitive 381 | description: 382 | name: http_parser 383 | sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" 384 | url: "https://pub.dev" 385 | source: hosted 386 | version: "4.0.2" 387 | io: 388 | dependency: transitive 389 | description: 390 | name: io 391 | sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" 392 | url: "https://pub.dev" 393 | source: hosted 394 | version: "1.0.4" 395 | js: 396 | dependency: transitive 397 | description: 398 | name: js 399 | sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 400 | url: "https://pub.dev" 401 | source: hosted 402 | version: "0.6.7" 403 | json_annotation: 404 | dependency: transitive 405 | description: 406 | name: json_annotation 407 | sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 408 | url: "https://pub.dev" 409 | source: hosted 410 | version: "4.8.1" 411 | lints: 412 | dependency: transitive 413 | description: 414 | name: lints 415 | sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" 416 | url: "https://pub.dev" 417 | source: hosted 418 | version: "2.1.1" 419 | logger: 420 | dependency: transitive 421 | description: 422 | name: logger 423 | sha256: "6bbb9d6f7056729537a4309bda2e74e18e5d9f14302489cc1e93f33b3fe32cac" 424 | url: "https://pub.dev" 425 | source: hosted 426 | version: "2.0.2+1" 427 | logging: 428 | dependency: transitive 429 | description: 430 | name: logging 431 | sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" 432 | url: "https://pub.dev" 433 | source: hosted 434 | version: "1.2.0" 435 | matcher: 436 | dependency: transitive 437 | description: 438 | name: matcher 439 | sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" 440 | url: "https://pub.dev" 441 | source: hosted 442 | version: "0.12.16" 443 | material_color_utilities: 444 | dependency: transitive 445 | description: 446 | name: material_color_utilities 447 | sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" 448 | url: "https://pub.dev" 449 | source: hosted 450 | version: "0.5.0" 451 | meta: 452 | dependency: transitive 453 | description: 454 | name: meta 455 | sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e 456 | url: "https://pub.dev" 457 | source: hosted 458 | version: "1.10.0" 459 | mime: 460 | dependency: transitive 461 | description: 462 | name: mime 463 | sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e 464 | url: "https://pub.dev" 465 | source: hosted 466 | version: "1.0.4" 467 | package_config: 468 | dependency: transitive 469 | description: 470 | name: package_config 471 | sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" 472 | url: "https://pub.dev" 473 | source: hosted 474 | version: "2.1.0" 475 | path: 476 | dependency: transitive 477 | description: 478 | name: path 479 | sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" 480 | url: "https://pub.dev" 481 | source: hosted 482 | version: "1.8.3" 483 | pedantic_mono: 484 | dependency: "direct dev" 485 | description: 486 | name: pedantic_mono 487 | sha256: b7ac7d8a9f8824ab053773599918d6c99d643ed63ce20b440a19fed028a80d63 488 | url: "https://pub.dev" 489 | source: hosted 490 | version: "1.24.0+1" 491 | petitparser: 492 | dependency: transitive 493 | description: 494 | name: petitparser 495 | sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750 496 | url: "https://pub.dev" 497 | source: hosted 498 | version: "5.4.0" 499 | pool: 500 | dependency: transitive 501 | description: 502 | name: pool 503 | sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" 504 | url: "https://pub.dev" 505 | source: hosted 506 | version: "1.5.1" 507 | pub_semver: 508 | dependency: transitive 509 | description: 510 | name: pub_semver 511 | sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" 512 | url: "https://pub.dev" 513 | source: hosted 514 | version: "2.1.4" 515 | pubspec_parse: 516 | dependency: transitive 517 | description: 518 | name: pubspec_parse 519 | sha256: c63b2876e58e194e4b0828fcb080ad0e06d051cb607a6be51a9e084f47cb9367 520 | url: "https://pub.dev" 521 | source: hosted 522 | version: "1.2.3" 523 | riverpod: 524 | dependency: transitive 525 | description: 526 | name: riverpod 527 | sha256: "08451ddbaad6eae73e2422d8109775885623340d721c6637b8719c9f4b478848" 528 | url: "https://pub.dev" 529 | source: hosted 530 | version: "2.4.8" 531 | riverpod_analyzer_utils: 532 | dependency: transitive 533 | description: 534 | name: riverpod_analyzer_utils 535 | sha256: d4dabc35358413bf4611fcb6abb46308a67c4ef4cd5e69fd3367b11925c59f57 536 | url: "https://pub.dev" 537 | source: hosted 538 | version: "0.5.0" 539 | riverpod_annotation: 540 | dependency: "direct main" 541 | description: 542 | name: riverpod_annotation 543 | sha256: "02c9bced96ed3ed8d9970820d1ce7b16600955bc01aa8b2276f09dd3d9d29ed9" 544 | url: "https://pub.dev" 545 | source: hosted 546 | version: "2.3.2" 547 | riverpod_generator: 548 | dependency: "direct dev" 549 | description: 550 | name: riverpod_generator 551 | sha256: "94b6c49bba879729611d690d434796e3b4e7c72a27e88b482b92c505e90f90d9" 552 | url: "https://pub.dev" 553 | source: hosted 554 | version: "2.3.8" 555 | riverpod_lint: 556 | dependency: "direct dev" 557 | description: 558 | name: riverpod_lint 559 | sha256: "6fc64ae102ba39b0889b7aa7f4ef6c5a8f71a2ad215b90c787f319a9407a128b" 560 | url: "https://pub.dev" 561 | source: hosted 562 | version: "2.3.6" 563 | roggle: 564 | dependency: "direct main" 565 | description: 566 | name: roggle 567 | sha256: d2b3550cf952cb7e55929c7f4b8e8e6cf1536b4652218e44846122c7bac78f45 568 | url: "https://pub.dev" 569 | source: hosted 570 | version: "0.4.2+1" 571 | rxdart: 572 | dependency: transitive 573 | description: 574 | name: rxdart 575 | sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb" 576 | url: "https://pub.dev" 577 | source: hosted 578 | version: "0.27.7" 579 | shelf: 580 | dependency: transitive 581 | description: 582 | name: shelf 583 | sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 584 | url: "https://pub.dev" 585 | source: hosted 586 | version: "1.4.1" 587 | shelf_web_socket: 588 | dependency: transitive 589 | description: 590 | name: shelf_web_socket 591 | sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" 592 | url: "https://pub.dev" 593 | source: hosted 594 | version: "1.0.4" 595 | sky_engine: 596 | dependency: transitive 597 | description: flutter 598 | source: sdk 599 | version: "0.0.99" 600 | source_gen: 601 | dependency: transitive 602 | description: 603 | name: source_gen 604 | sha256: fc0da689e5302edb6177fdd964efcb7f58912f43c28c2047a808f5bfff643d16 605 | url: "https://pub.dev" 606 | source: hosted 607 | version: "1.4.0" 608 | source_span: 609 | dependency: transitive 610 | description: 611 | name: source_span 612 | sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" 613 | url: "https://pub.dev" 614 | source: hosted 615 | version: "1.10.0" 616 | sprintf: 617 | dependency: transitive 618 | description: 619 | name: sprintf 620 | sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" 621 | url: "https://pub.dev" 622 | source: hosted 623 | version: "7.0.0" 624 | stack_trace: 625 | dependency: transitive 626 | description: 627 | name: stack_trace 628 | sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" 629 | url: "https://pub.dev" 630 | source: hosted 631 | version: "1.11.1" 632 | state_notifier: 633 | dependency: transitive 634 | description: 635 | name: state_notifier 636 | sha256: b8677376aa54f2d7c58280d5a007f9e8774f1968d1fb1c096adcb4792fba29bb 637 | url: "https://pub.dev" 638 | source: hosted 639 | version: "1.0.0" 640 | stream_channel: 641 | dependency: transitive 642 | description: 643 | name: stream_channel 644 | sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 645 | url: "https://pub.dev" 646 | source: hosted 647 | version: "2.1.2" 648 | stream_transform: 649 | dependency: transitive 650 | description: 651 | name: stream_transform 652 | sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" 653 | url: "https://pub.dev" 654 | source: hosted 655 | version: "2.1.0" 656 | string_scanner: 657 | dependency: transitive 658 | description: 659 | name: string_scanner 660 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" 661 | url: "https://pub.dev" 662 | source: hosted 663 | version: "1.2.0" 664 | term_glyph: 665 | dependency: transitive 666 | description: 667 | name: term_glyph 668 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 669 | url: "https://pub.dev" 670 | source: hosted 671 | version: "1.2.1" 672 | test_api: 673 | dependency: transitive 674 | description: 675 | name: test_api 676 | sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" 677 | url: "https://pub.dev" 678 | source: hosted 679 | version: "0.6.1" 680 | time: 681 | dependency: transitive 682 | description: 683 | name: time 684 | sha256: "83427e11d9072e038364a5e4da559e85869b227cf699a541be0da74f14140124" 685 | url: "https://pub.dev" 686 | source: hosted 687 | version: "2.1.3" 688 | timing: 689 | dependency: transitive 690 | description: 691 | name: timing 692 | sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32" 693 | url: "https://pub.dev" 694 | source: hosted 695 | version: "1.0.1" 696 | typed_data: 697 | dependency: transitive 698 | description: 699 | name: typed_data 700 | sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c 701 | url: "https://pub.dev" 702 | source: hosted 703 | version: "1.3.2" 704 | uuid: 705 | dependency: transitive 706 | description: 707 | name: uuid 708 | sha256: df5a4d8f22ee4ccd77f8839ac7cb274ebc11ef9adcce8b92be14b797fe889921 709 | url: "https://pub.dev" 710 | source: hosted 711 | version: "4.2.1" 712 | vector_math: 713 | dependency: transitive 714 | description: 715 | name: vector_math 716 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" 717 | url: "https://pub.dev" 718 | source: hosted 719 | version: "2.1.4" 720 | vm_service: 721 | dependency: transitive 722 | description: 723 | name: vm_service 724 | sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 725 | url: "https://pub.dev" 726 | source: hosted 727 | version: "13.0.0" 728 | watcher: 729 | dependency: transitive 730 | description: 731 | name: watcher 732 | sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" 733 | url: "https://pub.dev" 734 | source: hosted 735 | version: "1.1.0" 736 | web: 737 | dependency: transitive 738 | description: 739 | name: web 740 | sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 741 | url: "https://pub.dev" 742 | source: hosted 743 | version: "0.3.0" 744 | web_socket_channel: 745 | dependency: transitive 746 | description: 747 | name: web_socket_channel 748 | sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b 749 | url: "https://pub.dev" 750 | source: hosted 751 | version: "2.4.0" 752 | xml: 753 | dependency: transitive 754 | description: 755 | name: xml 756 | sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84" 757 | url: "https://pub.dev" 758 | source: hosted 759 | version: "6.3.0" 760 | yaml: 761 | dependency: transitive 762 | description: 763 | name: yaml 764 | sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" 765 | url: "https://pub.dev" 766 | source: hosted 767 | version: "3.1.2" 768 | sdks: 769 | dart: ">=3.2.0-194.0.dev <4.0.0" 770 | flutter: ">=3.7.0" 771 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_traffic_light 2 | description: A new Flutter project. 3 | publish_to: 'none' 4 | version: 1.0.0+1 5 | 6 | environment: 7 | sdk: '>=3.1.5 <4.0.0' 8 | 9 | dependencies: 10 | collection: 11 | cupertino_icons: 12 | flutter: 13 | sdk: flutter 14 | flutter_riverpod: 15 | flutter_web_plugins: 16 | sdk: flutter 17 | freezed_annotation: 18 | gap: 19 | riverpod_annotation: 20 | roggle: 21 | 22 | dev_dependencies: 23 | build_runner: 24 | custom_lint: 25 | flutter_gen_runner: 26 | flutter_test: 27 | sdk: flutter 28 | freezed: 29 | pedantic_mono: 30 | riverpod_generator: 31 | riverpod_lint: 32 | 33 | flutter: 34 | uses-material-design: true 35 | -------------------------------------------------------------------------------- /web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/susatthi/flutter-traffic-light/724e93b9461b4fc5594b9791b3d9874bcbb50d4b/web/favicon.png -------------------------------------------------------------------------------- /web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/susatthi/flutter-traffic-light/724e93b9461b4fc5594b9791b3d9874bcbb50d4b/web/icons/Icon-192.png -------------------------------------------------------------------------------- /web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/susatthi/flutter-traffic-light/724e93b9461b4fc5594b9791b3d9874bcbb50d4b/web/icons/Icon-512.png -------------------------------------------------------------------------------- /web/icons/Icon-maskable-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/susatthi/flutter-traffic-light/724e93b9461b4fc5594b9791b3d9874bcbb50d4b/web/icons/Icon-maskable-192.png -------------------------------------------------------------------------------- /web/icons/Icon-maskable-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/susatthi/flutter-traffic-light/724e93b9461b4fc5594b9791b3d9874bcbb50d4b/web/icons/Icon-maskable-512.png -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 信号機シミュレーター 33 | 34 | 35 | 39 | 40 | 41 | 42 | 43 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "信号機シミュレーター", 3 | "short_name": "信号機シミュレーター", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "Flutter における「状態」を解説するためのサンプル。Riverpod を利用しています。", 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 | } 36 | --------------------------------------------------------------------------------