Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Session/8 #21

Merged
merged 34 commits into from
Sep 27, 2023
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
15f36d9
Add riverpod related packages
Sep 13, 2023
97eba52
Wrap with ProviderScope
Sep 13, 2023
65d4fe1
Add lint rule: prefer absolute path
Sep 15, 2023
e0fd43e
Enable riverpod_lint
Sep 15, 2023
965cb3a
Implement YumemiWeather DI
Sep 15, 2023
6215fd3
Update fetchWeather reference
Sep 15, 2023
cb4d385
Create UiState
Sep 15, 2023
02ecb06
Update weather_page.dart based on UiState
Sep 15, 2023
70b4d65
Update weather_forecast.dart based on UiState
Sep 15, 2023
57d2d55
Add build procedure on README.md
Sep 18, 2023
4146055
Rename provider and file name: UiState(ui_state.dart) -> WeatherState…
Sep 18, 2023
9b39dc4
Create ARCHITECTURE.md
Sep 18, 2023
1d0133d
Fix lint warnings
Sep 18, 2023
464db20
Edit image size
Sep 18, 2023
1bb46f6
Edit ARCHITECTURE.md
Sep 18, 2023
52de150
Update doc/ARCHITECTURE.md
mqkotoo Sep 20, 2023
7d4a11f
Edit lint rule
Sep 20, 2023
4338e84
Fix: ref.read -> ref.watch
Sep 20, 2023
8533bd3
Fix: throw UnimplementedError -> return YumemiWeather()
Sep 20, 2023
8ad3a27
Update README.md
Sep 20, 2023
9a1e8a0
Add graph.md
Sep 20, 2023
b91f7ce
Rename: WeatherState -> WeatherStateNotifier
Sep 22, 2023
f349515
Rename: WeatherData -> WeatherForecast(model)
Sep 22, 2023
40bd88f
Rename: WeatherForecast -> WeatherForecastPanel(component) to avoid n…
Sep 22, 2023
2a40bf0
Update ARCHITECTURE.md
Sep 22, 2023
b35e4a4
Add duplicate_ignore to fix warnings
Sep 26, 2023
516fe44
Move YumemiWeatherClientProvider to weather_service.dart
Sep 26, 2023
c9e20c5
Rename file: weather_state.dart -> weather_state_notifier.dart
Sep 26, 2023
4391026
Update ARCHITECTURE.md
Sep 26, 2023
2494991
Update graph.md
Sep 26, 2023
1f88e72
Rename: weatherData -> weatherState
Sep 26, 2023
2de8a51
Delete graph.md
Sep 27, 2023
d297f58
Update ARCHITECTURE.md
Sep 27, 2023
177b379
Update ARCHITECTURE.md
Sep 27, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 36 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,37 @@
# flutter_training
# ゆめみ Flutter研修課題

A new Flutter project.
テンプレート

- https://github.com/yumemi-inc/flutter-training-template

## 環境構築

*リポジトリをクローン

```
git clone https://github.com/mqkotoo/flutter_training.git
```

* 作業ディレクトリを変更する

```
cd flutter_training
```

* fvmに指定されたバージョンのFlutterをインストールする

```
fvm install
```

* 依存パッケージをインストールする

```
fvm flutter pub get
```

* ビルドラン

```
fvm flutter run
```
4 changes: 4 additions & 0 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,7 @@ include: package:blendthink_lints/recommended.yaml
linter:
rules:
sort_pub_dependencies: false

analyzer:
plugins:
- custom_lint
trm11tkr marked this conversation as resolved.
Show resolved Hide resolved
94 changes: 94 additions & 0 deletions doc/ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
## アーキテクチャ
trm11tkr marked this conversation as resolved.
Show resolved Hide resolved

今回のプロジェクトではあえて、いわゆるアーキテクチャというような大掛かりな設計はせずに、[`WeatherStateNotifier`](../lib/state/weather_state.dart)
という、天気の取得操作、結果を管理するプロバイダーを作成し、Viewレイヤとやりとりするだけのシンプルな設計にしました。

### ルール

* Stateレイヤでなんらかの状態を操作して保管する。
* Viewレイヤから`ref.read`してStateの操作、`ref.watch`してStateの表示を行う。
* Stateレイヤのロジックが肥大する場合はServiceレイヤに移動させる。

上記のルールに基づいて開発を進めました。

### モチベーション

* 今回のような規模が小さいプロジェクトに、たくさんレイヤーがあるアーキテクチャを採用すると無駄なレイヤーが増え、複雑化する。
* 無理やり一定のアーキテクチャの型にはめると、個人的にはまだアーキテクチャの知識が浅いので、実際の必要性を感じることなく形だけのアーキテクチャになる可能性がある。
* 簡潔でシンプルでわかりやすい。(主観になりますが。。)


## View

* 天気の表示、エラーのダイアログを表示。 天気を取得するボタンを提供。

### WeatherPage

* アプリのメイン画面
* `Reload`ボタンを押して、`weatherStateProvider`をreadして、天気の取得処理を行う。
* `weatherStateProvider`で天気取得に失敗した場合は、エラーメッセージを表示する。

### WeatherForecastPanel

* 天気の情報、気温等を表示しているコンポネント。
* `weatherStateNotifierProvider`をwatchして、天気の状態を表示する。

## State

* 天気の情報を取得し取得した天気のデータを保持するメソッドを提供

### WeatherStateNotifier

* 天気の取得が成功したらそのデータを格納し、失敗した場合は、`onError`関数を引数で受け取る。

## Service

* `WeatherStateNotifier`での処理が肥大化したので天気を取得する処理はここに移動した。

## Model

* 天気情報やリクエストを送るモデルを定義、変換処理等を定義

### WeatherCondition

* 天気のステータスを定義、取得した天気に応じてイメージを返すextensionを定義

### WeatherForecast

* 天気の情報、最低気温、最高気温、日にちを定義
* データの変換処理を定義

### WeatherRequest

* リクエストを送るクラスを定義

```mermaid
trm11tkr marked this conversation as resolved.
Show resolved Hide resolved
flowchart TB
subgraph Arrows
direction LR
start1[ ] -..->|read| stop1[ ]
style start1 height:0px;
style stop1 height:0px;
start2[ ] --->|listen| stop2[ ]
style start2 height:0px;
style stop2 height:0px;
start3[ ] ===>|watch| stop3[ ]
style start3 height:0px;
style stop3 height:0px;
end
subgraph Type
direction TB
ConsumerWidget((widget));
Provider[[provider]];
end

weatherStateProvider[["weatherStateProvider"]];
trm11tkr marked this conversation as resolved.
Show resolved Hide resolved
weatherServiceProvider[["weatherServiceProvider"]];
yumemiWeatherClientProvider[["yumemiWeatherClientProvider"]];
WeatherForecast((WeatherForecast));
WeatherPage((WeatherPage));

weatherStateProvider ==> WeatherForecast;
weatherStateProvider -.-> WeatherPage;
yumemiWeatherClientProvider ==> weatherServiceProvider;
```
30 changes: 30 additions & 0 deletions doc/graph.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
```mermaid
trm11tkr marked this conversation as resolved.
Show resolved Hide resolved
flowchart TB
subgraph Arrows
direction LR
start1[ ] -..->|read| stop1[ ]
style start1 height:0px;
style stop1 height:0px;
start2[ ] --->|listen| stop2[ ]
style start2 height:0px;
style stop2 height:0px;
start3[ ] ===>|watch| stop3[ ]
style start3 height:0px;
style stop3 height:0px;
end
subgraph Type
direction TB
ConsumerWidget((widget));
Provider[[provider]];
end

weatherStateProvider[["weatherStateProvider"]];
weatherServiceProvider[["weatherServiceProvider"]];
yumemiWeatherClientProvider[["yumemiWeatherClientProvider"]];
WeatherForecast((WeatherForecast));
WeatherPage((WeatherPage));

weatherStateProvider ==> WeatherForecast;
weatherStateProvider -.-> WeatherPage;
yumemiWeatherClientProvider ==> weatherServiceProvider;
```
8 changes: 7 additions & 1 deletion lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_training/view/launch_page.dart';

void main() {
runApp(const MainApp());
WidgetsFlutterBinding.ensureInitialized();
runApp(
const ProviderScope(
child: MainApp(),
),
);
}

class MainApp extends StatelessWidget {
Expand Down
14 changes: 7 additions & 7 deletions lib/model/weather_data.dart → lib/model/weather_forecast.dart
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import 'package:flutter_training/model/weather_condition.dart';
import 'package:freezed_annotation/freezed_annotation.dart';

part 'weather_data.freezed.dart';
part 'weather_forecast.freezed.dart';

part 'weather_data.g.dart';
part 'weather_forecast.g.dart';

@freezed
class WeatherData with _$WeatherData {
const factory WeatherData({
class WeatherForecast with _$WeatherForecast {
trm11tkr marked this conversation as resolved.
Show resolved Hide resolved
const factory WeatherForecast({
trm11tkr marked this conversation as resolved.
Show resolved Hide resolved
required WeatherCondition weatherCondition,
required int maxTemperature,
required int minTemperature,
required DateTime date,
}) = _WeatherData;
}) = _WeatherForecast;

factory WeatherData.fromJson(Map<String, dynamic> json) =>
_$WeatherDataFromJson(json);
factory WeatherForecast.fromJson(Map<String, dynamic> json) =>
_$WeatherForecastFromJson(json);
}
Loading
Loading