必死に調べた時間が無駄になったオチ
(追記)
VSCodeのクイックフィックス使おうね
# VSCodeあまり使わないからこんな機能あるの知らなったの…
以下は自分の調べた内容を無駄にするのも嫌だったので残します。
Flutterを初めた
色々あってFlutterを触ってみることになった。
スマホアプリ開発は全くの未経験のため、AWSサービスを頼って色々やろうと思った
あつらえ向きのガイドがあった
流石はAWS
が、問題発生
The parameter 'key' can't have a value of 'null' because of its type, but the implicit default value is 'null'.
Try adding either an explicit non-'null' default value or the 'required' modifier.
The property 'authFlowStatus' can't be unconditionally accessed because the receiver can be 'null'.
Try making the access conditional (using '?.') or adding a null check to the target ('!')
原因はFlutter/Dartのアップデート
色々調べてみたところnull-safetyが原因だと分かった
Flutter2の登場
AWSのサンプルに限らず現時点(2021/06/08)で表に出ている多くのFlutterやってみた系の記事のコードや、書籍の記載内容なども含めてこの手のエラーが発生する。
# 恐らくFlutter1.X時代に記載された記事が多いからだと思われる。
で、結局どうすればエラー解決すんの?
それを調べていたら下記にたどり着いた
Flutter2のDart Null Safetyを既存のプロジェクトに導入する
エラー修正方法 2 required キーワードを設定する
エラー修正方法 3 Optional 型でラップをする
エラー1は上記記事の記載そのままで解決する。どちらかでOKだ
final VoidCallback shouldShowSignUp;
LoginPage({required Key key,required this.shouldShowSignUp}) : super(key: key);
final VoidCallback? shouldShowSignUp;
LoginPage({Key? key, this.shouldShowSignUp}) : super(key: key);
Optional型にするとnullチェックのif文を作成しないとnullエラーが発生する可能性はあるが、過去に触ってきた言語でnullチェック文を書いてきた人間からするとこちらの方が取っつきやすいのではないだろうか。
エラー2は上記の方法では解決できない。
、ここでエラーを吐いている原因はstream:
オプションで指定したストリームから取得されるsnapshotのパラメータである。
# コード全体は原文のAWSページを見て頂きたい。
home: StreamBuilder<AuthState>(
// 2
stream: _authService.authStateController.stream,
builder: (context, snapshot) {
// 3
if (snapshot.hasData) {
return Navigator(
pages: [
// 4
// Show Login Page
if (snapshot.data.authFlowStatus == AuthFlowStatus.login)
MaterialPage(child: LoginPage()),
// 5
// Show Sign Up Page
if (snapshot.data.authFlowStatus == AuthFlowStatus.signUp)
MaterialPage(child: SignUpPage())
],
onPopPage: (route, result) => route.didPop(result),
);
} else {
// 6
return Container(
alignment: Alignment.center,
child: CircularProgressIndicator(),
);
}
}),
StreamControllerのstreamはnullableである
stream → Stream?
The asynchronous computation to which this builder is currently connected, possibly null. When changed, the current summary is updated using afterDisconnected, if the previous stream was not null, followed by afterConnected, if the new stream is not null.
そのためそこから取得されるsnapshotもnullableなのだと思われる。
# Flutterの公式リファレンスを読んでも細かい記載が見当たらなかったためあくまで推定、有識者の見解ができればほしい
パラメータがnullableでif文に乗せれないなら、変数に入れてしまえばいいじゃない
変数に入れようにも該当のif文がある場所はpagesのList内のため変数定義はできない。
ならどうするか?MaterialPageを返す関数を定義すればよいのである
MaterialPage snapshotPage(snapshot){
AuthFlowStatus status = snapshot.data.authFlowStatus;
if (status == AuthFlowStatus.login)
return MaterialPage(
child: LoginPage()
);
if (status == AuthFlowStatus.signUp)
return MaterialPage(
child: SignUpPage()
);
// 何も返さない場合は関数がエラーになるため、空ページを返す
return MaterialPage(child: Scaffold());
}
...
home: StreamBuilder<AuthState>(
// 2
stream: _authService.authStateController.stream,
builder: (context, snapshot) {
// 3
if (snapshot.hasData) {
return Navigator(
pages: [
snapshotPage(snapshot)
],
onPopPage: (route, result) => route.didPop(result),
);
}
...
# と思っていたのだが、なぜか関数に引数として渡した後ならif文にそのまま載せてもエラーにはならない
これはDart/Flutter初学者である私には原因がわからない。
(2022/11追記) コードに記載している関数は引数の型を省略しているので分かりにくいが、システム的には下記のように認識されている。
要はnullを許容するdynamic?型ではなく、nullを許容できないdynamic型として認識されているため、nullを考慮することなくコーディングできている訳だ
MaterialPage snapshotPage(dynamic snapshot){...}
MaterialPage snapshotPage(snapshot){
if (snapshot.data.authFlowStatus == AuthFlowStatus.login)
return MaterialPage(
child: LoginPage()
);
if (snapshot.data.authFlowStatus == AuthFlowStatus.signUp)
return MaterialPage(
child: SignUpPage()
);
// 何も返さない場合は関数がエラーになるため、空ページを返す
return MaterialPage(child: Scaffold());
}
なにはともあれ解決
エラーが解決し、サンプルのアプリも動くことが確認できた。