【Flutter】設定機能を構築してみる
まえがき
いきなり入力機能追加に伴い、はじめて設定機能を構築しました。
設定の保存にはshared_preferencesが便利なのは知ってたけど使い方がわからない。
そこでいまはやりのChatGPTに聞いてみました。
とりあえずダイレクトに聞いたら、なかなかいい答えが返ってきた。
だけどproviderを使って状態管理してるからStatefulWidgetじゃ使えないから聞き直し。
質問の仕方が悪くてごめんね。
そうしたらほぼ求めてる答えが返ってきました。
あとはジChatGPTが書いてくれたコードをコピペしてっと。
コードにちょっと手を加えたらあっさり完成しました。
そうだ、これを記事にしよう!!
と思ったらChatGPTに聞いた履歴を消してしまってた・・・。
せっかくのChatGPTネタだったのにぃ〜。
はい!というわけで、起動時に表示する画面を切り替える設定を構築します。
具体的にはページAとページBと設定画面を用意して設定画面で起動時の画面をAとBで切り替えできるようにします。
それでは始めましょう。
プロジェクトを作成
まずはプロジェクト名settings_sampleで新規プロジェクトを作成します。
以下の記事を参考にしてプロジェクト名をsettings_sampleに置き換えて作成してください。
またプロジェクトのテンプレートを「Application(empty)」を選択すると最小限のコードが生成されるのでこちらを選びます。

パッケージをインストール
今回は設定の保存に使うshared_preferencesと状態管理のproviderをpubspec.yamlに追加します。

(https://pub.dev/packages/shared_preferences/install)

(https://pub.dev/packages/provider/install)
ターミナルから以下のコマンドを実行します。
flutter pub add shared_preferences
flutter pub add provider
またはpubspec.yamlのdependenciesのところにshared_preferences: ^2.0.18とprovider: ^6.0.5を入力してターミナルでflutter pub getを実行します。
dependencies:
shared_preferences: ^2.0.18
provider: ^6.0.5
ファイル構成
-
lib-
viewpage_a.dartpage_b.dartsettings.dart
-
viewmodelsetting_model.dart
main.dart
-
libディレクトリ内にviewディレクトリとviewmodelディレクトリを作成します。
viewディレクトリ内には各画面を作成します。
viewmodelディレクトリ内にはSettingModelを作成します。
viewディレクトリ内のファイルを作成
ページAとページB
ページAをファイル名page_a.dartで作成、ページBをファイル名page_b.dartで作成します。
import "package:flutter/material.dart";
class PageA extends StatelessWidget {
const PageA({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: IconButton(
onPressed: () {},
icon: const Icon(
Icons.settings,
),
),
),
body: const Center(
child: Text(
"PageA",
style: TextStyle(
fontSize: 60,
color: Colors.red,
),
),
),
);
}
}
import "package:flutter/material.dart";
class PageB extends StatelessWidget {
const PageB({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: IconButton(
onPressed: () {},
icon: const Icon(
Icons.settings,
),
),
),
body: const Center(
child: Text(
"PageB",
style: TextStyle(fontSize: 60),
),
),
);
}
}
ページA、Bと両方のアプリケーションバーと画面中央に"PageA"、"PageB"を表示します。
まずはAppBarウィジェットを使ってアプリケーションバーをおきます。
アプリケーションバーにはleadingプロパティでIconButtonクラスを呼び出します。
leadingプロパティはアプリケーションバーの左側にアイコンを表示できます。
onPressedプロパティはアイコンがタップされたときに実行される動作を指定します。
とりあえず、(){}としてタップだけ有効にしておきます。
iconプロパティでIconクラスを呼び出しIcons.settingsを表示します。
Icons.settingsは歯車のアイコンを表示します。
あとで歯車のアイコンをタップして設定画面に遷移するようにコードを追加します。
次にCenterウィジェットを使って画面中央にテキストを置くことができます。
そしてTextウィジェットを使って各テキストを表示します。
styleプロパティからTextStyleクラスを呼び出してfontSizeプロパティを60にしてフォントサイズを変更します。
また"PageA"だけcolorプロパティをColors.redにしてフォントカラーを赤色にして区別しやすくします。
設定画面
設定画面をファイル名setting.dartで作成します。
import "package:flutter/material.dart";
class SettingPage extends StatelessWidget {
const SettingPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
children: const [
ListTile(
title: Text("起動時画面切り替え"),
subtitle: Text("起動時の画面をAとBで切り替えます"),
trailing: Switch(
value: true,
onChanged: null,
),
),
],
),
);
}
}
main.dartを変更
まずはmain.dartを以下のようにスッキリしておきます。
import "package:flutter/material.dart";
import "package:settings_sample/views/page_a.dart";
void main() {
runApp(const MainApp());
}
class MainApp extends StatelessWidget {
const MainApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: PageA(),
);
}
}
import 'package:settings_sample/views/page_a.dart';を追加します。
page_a.dartのPageAウィジェットを参照します。
MainAppはStatelessWidgetに変更します。
MainAppのMaterialAppウィジェットを返します。
Scaffoldウィジェットを子要素としてbodyプロパティからPageAウィジェットを使用します。
これにより、PageAがアプリケーションのメイン画面に表示されます。
設定画面に移動
歯車のアイコンをタップして設定画面へ移動できるようにします。
以下のFlutterのドキュメントを参考にして実装します。
ドキュメントを見ると以下のコードを追加するようになっています。
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const SecondRoute()),
);
}
ここではSecondRoute()をSettingPage()に置き換えて、page_a.dartとpage_b.dartのonPressedに追加します。
onPressed: () {
+ Navigator.push(
+ context,
+ MaterialPageRoute(builder: (context) => const SettingPage()),
+ );
}
あと、setting.dartをインポートします。
+ import "package:settings_sample/views/setting.dart";
ViewModelを作成
設定画面での設定状態をshared_preferencesで保存します。
保存したデータをproviderで状態管理するためにsetting_model.dartを作成します。
import "package:flutter/material.dart";
import "package:shared_preferences/shared_preferences.dart";
class SettingModel with ChangeNotifier {
// 初期値
bool _startPageA = true;
bool get startPageA => _startPageA;
Future<void> getSetting() async {
final prefs = await SharedPreferences.getInstance();
final startPageAValue = prefs.getBool("startPageA") ?? false;
_startPageA = startPageAValue;
notifyListeners();
}
Future<void> setStartPageA(bool value) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setBool("startPageA", value);
_startPageA = value;
notifyListeners();
}
}
アプリがPageAで開始するかどうかを決定するbool型の_startPageA変数があります。
また初期値はtrueにしてページAが表示されるようにしています。
getterメソッドで_startPageAの値を取得します。
getSetting()メソッドはSharedPreferencesからユーザーの保存された設定を取得し_startPageA変数を更新します。
setStartPageA()メソッドは_startPageA変数を更新しSharedPreferencesに保存します。
変数_startPageAに変更がある場合にはnotifyListeners()メソッドが呼び出されて状態に変更があったことをリスナーに通知します。
スイッチの設定
設定画面のスイッチを動作するようにsetting.dartを変更します。
import "package:flutter/material.dart";
+ import "package:provider/provider.dart";
+ import "package:settings_sample/viewmodels/setting_model.dart";
class SettingPage extends StatelessWidget {
const SettingPage({super.key});
@override
Widget build(BuildContext context) {
- return Scaffold(
- body: ListView(
- children: const [
- ListTile(
- title: Text("起動時画面切り替え"),
- subtitle: Text("起動時の画面をAとBで切り替えます"),
- trailing: Switch(
- value: true,
- onChanged: null,
- ),
- ),
- ],
- ),
- );
+ return ChangeNotifierProvider<SettingModel>(
+ create: (_) => SettingModel()..getSetting(),
+ child: Scaffold(
+ body: Consumer(
+ builder: (
+ BuildContext context,
+ SettingModel model,
+ Widget? child,
+ ) =>
+ ListView(
+ children: [
+ ListTile(
+ title: const Text("起動時画面切り替え"),
+ subtitle: const Text("起動時の画面をAとBで切り替えます"),
+ trailing: Switch(
+ value: model.startPageA,
+ onChanged: (value) => {
+ model.setStartPageA(value),
+ debugPrint(value.toString()),
+ }
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ );
}
}
SettingModelクラスのインスタンスを作成しgetSetting()メソッドを呼び出してアプリの設定情報を取得します。
そしてChangeNotifierProviderを使用してSettingModelクラスのインスタンスを作成します。
これによりアプリ内の他のウィジェットでSettingModelクラスのインスタンスを使用して設定情報を取得できます。
ConsumerウィジェットはChangeNotifierProviderで状態を監視し変更された場合にUIを再ビルドするために使用されます。
このコードではSettingModelクラスのインスタンスを使用してアプリの設定情報を取得しUIに反映します。
builderプロパティはUIを構築するためのコールバック関数を指定します。
このコールバック関数はBuildContext、SettingModel、およびchildウィジェットを引数として受け取りUIを構築するために使用されます。
このように、Consumerウィジェットを使用することでアプリ内の他のウィジェットでSettingModelクラスのインスタンスを使用してアプリの設定情報を取得できます。
SettingModelクラスのstartPageAプロパティを使用してスイッチの初期値を設定します。
onChangedプロパティはスイッチの値が変更されたときに呼び出されるコールバック関数を指定します。
このコードではSettingModelクラスのsetStartPageAメソッドを使用してスイッチの値を更新します。
そしてdebugPrint関数を使用してスイッチの値をデバッグコンソールに出力します。
それでは一度実行してみましょう。
そして設定画面に移動してスイッチを動かしてみます。
そうするとデバッグコンソールでスイッチを動かすとtrueかfalseを交互に表示します。
main.dartを再び変更
最後にトップページを設定の値によって変更されるようにします。
import "package:flutter/material.dart";
+ import "package:provider/provider.dart";
+ import "package:settings_sample/viewmodels/setting_model.dart";
import "package:settings_sample/views/page_a.dart";
+ import "package:settings_sample/views/page_b.dart";
void main() {
runApp(const MainApp());
}
class MainApp extends StatelessWidget {
const MainApp({super.key});
@override
Widget build(BuildContext context) {
- return const MaterialApp(
+ return ChangeNotifierProvider<SettingModel>(
- home: PageA(),
+ create: (_) => SettingModel()..getSetting(),
+ child: MaterialApp(
+ home: Consumer(
+ builder: (
+ BuildContext context,
+ SettingModel model,
+ Widget? child,
+ ) =>
+ Scaffold(
+ body: model.startPageA ? const PageA() : const PageB(),
+ ),
+ ),
+ ),
);
}
}
setting.dartを変更と同様にChangeNotifierProviderを使用してSettingModelクラスのインスタンスを作成してConsumerウィジェットを導入します。
bodyプロパティには3項演算子を利用して"PageA"と"PageB"を切り替えます。
3項演算子とはif-else文と同じように条件分岐を行う演算子です。
具体的には次のように書くことができます。
条件式 ? 式1 : 式2
この式では条件式がtrueならば式1が選択されます。条件式がfalseならば式2が選択されます。
model.startPageA ? const PageA() : const PageB()
今回の場合はmodel.startPageAがtrueでならばPageA()をfalseならばPageB()が選択されます。
これで設定画面から起動時の画面を設定できるようになりました。
スマホアプリ「ひとこと投資メモ」シリーズをリリース
Flutter学習のアウトプットの一環として「日本株ひとこと投資メモ」「米国株ひとこと投資メモ」を公開しています。
今回の設定機能の構築も活用しています。
簡単に使えるライトな投資メモアプリです。
iPhone、Android両方に対応しています。
みなさんの投資ライフに少しでも活用していただきれば幸いです。
以下のリンクからそれぞれのサイトに移動してダウンロードをお願いします。
Discussion