はじめに
Udemyの練習問題の備忘録です.課題はStopWatchの機能の作成です.
今回問題の達成目標としては,自分でコードを書いてわからないところを自分の言葉で解釈できることとする.かなり,初歩的なことで,つたない内容となっております.
内容
私が最初の1時間で書いたコード
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:stop_watch_timer/stop_watch_timer.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'ストップウォッチ',
theme: ThemeData(
useMaterial3: true,
),
home: const Watches(),
);
}
}
class Watches extends StatefulWidget {
const Watches({super.key});
@override
State<Watches> createState() => _WatchesState();
}
class _WatchesState extends State<Watches> {
final StopWatchTimer _stopWatchTimer = StopWatchTimer();
@override
void initState(){
super.initState();
}
@override
void dispose() async{
super.dispose();
await _stopWatchTimer.dispose();
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('ストップウォッチ'),
),
body: Align(
alignment: Alignment.center,
child:
Column(children: [
Text('経過時間'),
Text(
StopWatchTimer.getDisplayTime(_stopWatchTimer.rawTime.value)),
Center(
child: Row(
children: [
ElevatedButton(
onPressed: (){_stopWatchTimer.onStartTimer;
setState(() {
_stopWatchTimer.rawTime.value;
});
},
child: const Text('Start')),
ElevatedButton(
onPressed: (){_stopWatchTimer.onStopTimer;
setState(() {
_stopWatchTimer.rawTime.value;
});
},
child: const Text('Stop')),
ElevatedButton(
onPressed: (){_stopWatchTimer.onResetTimer;
setState(() {
_stopWatchTimer.rawTime.value;
});
},
child: const Text('Reset')),
],
),
),
],)
),
);
}
}
上記が1時間かけて開発したコードです.実装してわかるのですが,ボタンを押しても動かないという欠点があります.
ほんとならもっと時間をかけて何故動かないかを自分なりに考えるべきなのでしょうが,知らないことを考えてもわかるわけないので,取り合えず答えを見たり,Geminiに聞きました.
答えを見て
反省点として以下の内容が挙げられます.
- オブジェクト指向を知らなかった.
- 関数の使用の仕方が悪い
- 時間の更新をしていない
- setStateの誤用
オブジェクト指向を知らなかった
コードの並びがよくわからなかったが,これがオブジェクト指向なのではと思い,コードをある程度のまとまりとして分割し,模してなパーツとして組手立てていることに気が付いた.
関数の使用の仕方が悪い
答えのコードでは関数を呼び出して,コードを実行しているつもりが,私のコードだとそのまま実行しているつもりになっている(実際には関数は呼ばれているだけで,実行されていないかった.).これのデメリットは再利用性が低いことです.今回はこれで終わりだからいいけど,再利用を考えるなら関数化する必要があると思いました.
setStateの誤用
まずsetStateのことを私はそのWidget内で宣言すれば,いいんだろと認識していたがその点が認識不足だった.
つまり,setStateはFlutterフレームワークに対して,Widgetの状態が変更されたことを明示的に発見させる役目,つまり変化のフラッグを認識する機能です.したがって,変化させた後に呼び出さなければならなかった.
また,関数を呼び出して実行するには以下のようにすればよかった.
ElevatedButton(
onPressed: (){_stopWatchTimer.onStartTimer;
setState(() {
_stopWatchTimer.rawTime.value;
});
},
child: const Text('Start')),
を
ElevatedButton(
onPressed: () {
_stopWatchTimer.onStartTimer();
},
child: const Text('Start'),
),
である.
ちなみにGemini曰く,下記の新しいプロジェクトを立ち上げたときに出るコートについて
class _WatchesState extends State<Watches> {
int _count = 0;
void _incrementCounter() {
setState(() {
_count++;
});
}
// ...
}
これの場合は,
-
_incrementCounter
関数が呼び出される。 -
setState
が呼び出される。 -
setState
のコールバック関数が実行され、_count++
によって_count
の値が変更される。 - Flutterフレームワークは、
setState
によって状態が変更されたことを検知する。
という関数→setState内のcallback関数→setStateによるFlutterframeへの通知という順番らしい.
その点を踏まえて私の最初に記述したものを見ると
onPressed: (){
_stopWatchTimer.onStartTimer;
setState(() {
_stopWatchTimer.rawTime.value;
});
関数を呼んで,値をcallback?というよくわからないことをしている.私としては関数を呼んで明示的にsetStateで値を渡している気になっていたが,その点が問題であった.つまり,渡すのではなく,変化を通知するということを理解していなかった.
またgeminiによる回答では,StreamBuildという非同期関数でbuildが行われていた.
StreamBuilder<int>(
stream: _stopWatchTimer.rawTime,
initialData: _stopWatchTimer.rawTime.value,
builder: (context, snapshot) {
final value = snapshot.data!;
return Text(StopWatchTimer.getDisplayTime(value)
},
),
上記はストリームとは何かについてまた別で勉強しないといけなさそうなので,また新しく勉強内容が増えたようだ.成長の余地を見つけたとしてうれしく思うと前向きにとらえるものとする.
まとめ
Udemyの動画ではズオン形式で勉強しつつ,わからないことを生成AIで詳しく深堀していく勉強法は,私に適していると思う.生成AIは信用ならないから使わないという人が身近にいるが,そのそもそもネットも100%信用できないし,間違えたら方針転換やそれなりの方策に従ってればいいと思う今日このごろ.