flutter_bloc
を使って画面遷移を行う場合、Blocの状態を使ってナビゲーションのロジックを制御することができます。
以下は、flutter_bloc
を使用して画面遷移を実装するためのサンプルコードとその解説です。
このサンプルでは、ログイン画面からホーム画面への遷移を実装します。
1.サンプル(動作)
以下は、2つの画面(PageA画面(page_a.dart)、PageB画面(page_b.dart))を用意し、相互の画面遷移を行うサンプルです。
①PageA画面で、ElevatedButton「Go to PageB」をクリックします。
②するとPageB画面に遷移します。
ここで、ElevatedButton「Go to PageA」をクリックします。
③するとPageA画面に遷移し(戻り)ます。
2.サンプル(コード)
2ー1.ファイルの配置
各ファイルを以下の様に配置することとします。
lib/
├── main.dart
├── navigation_event.dart
├── navigation_state.dart
├── navigation_bloc.dart
├── page_a.dart
└── page_b.dart
2ー2.各ファイルのコード
(1)main.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; // Blocを使用するためのパッケージをインポート
import 'navigation_bloc.dart'; // NavigationBlocをインポート
import 'navigation_state.dart'; // NavigationStateをインポート
import 'page_a.dart'; // PageAのウィジェットをインポート
import 'page_b.dart'; // PageBのウィジェットをインポート
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
// BlocProviderを使ってNavigationBlocをアプリ全体に提供
return BlocProvider(
create: (context) => NavigationBloc(),
// MaterialAppはFlutterアプリの基本的な構造を提供
child: const MaterialApp(
// 最初に表示するウィジェットをNavigationに設定
home: Navigation(),
),
);
}
}
class Navigation extends StatelessWidget {
const Navigation({super.key});
@override
Widget build(BuildContext context) {
// BlocBuilderは、NavigationBlocの状態に応じてUIを再構築
return BlocBuilder<NavigationBloc, NavigationState>(
builder: (context, state) {
if (state is PageA) {
// stateがPageAのとき、PageAのウィジェットを表示
return const PageAScreen();
} else if (state is PageB) {
// stateがPageBのとき、PageBのウィジェットを表示
return const PageBScreen();
}
// それ以外の状態(初期状態など)はローディングインジケーターを表示
return const Scaffold(body: Center(child: CircularProgressIndicator()));
},
);
}
}
(2)navigation_event.dart
// イベントの基底クラスを定義します。
// すべてのナビゲーションイベントはこのクラスを継承します。
// イベントクラスは、ユーザーのアクションやアプリの状態変化
// を表現するために使用されます。
abstract class NavigationEvent {}
// PageAに遷移するためのイベントクラスです。
// このイベントが発行されると、アプリはPageAに遷移します。
class NavigateToPageA extends NavigationEvent {}
// PageBに遷移するためのイベントクラスです。
// このイベントが発行されると、アプリはPageBに遷移します。
class NavigateToPageB extends NavigationEvent {}
(3)navigation_state.dart
// 状態の基底クラスを定義します。
// すべてのナビゲーション状態はこのクラスを継承します。
abstract class NavigationState {}
// PageAの状態を表すクラスです。
// このクラスは、現在アプリがPageAにいることを示します。
class PageA extends NavigationState {}
// PageBの状態を表すクラスです。
// このクラスは、現在アプリがPageBにいることを示します。
class PageB extends NavigationState {}
(4)navigation_bloc.dart
import 'package:bloc/bloc.dart'; // Blocパッケージをインポート
import 'navigation_event.dart'; // NavigationEventをインポート
import 'navigation_state.dart'; // NavigationStateをインポート
// NavigationBlocクラスは、ナビゲーションイベントとナビゲーション状態を管理するBlocです。
class NavigationBloc extends Bloc<NavigationEvent, NavigationState> {
// コンストラクタで初期状態をPageAに設定します。
NavigationBloc() : super(PageA()) {
// on<NavigateToPageA>メソッドは、NavigateToPageAイベントが発生したときにPageA状態に遷移するようにします。
on<NavigateToPageA>((event, emit) => emit(PageA())); // 状態をPageAに遷移させます。
// on<NavigateToPageB>メソッドは、NavigateToPageBイベントが発生したときにPageB状態に遷移するようにします
on<NavigateToPageB>((event, emit) => emit(PageB())); // 状態をPageBに遷移させます。
}
}
// このように、NavigationBlocはナビゲーションイベントに応じて状態を変更し、それに基づいてUIを更新します。
// これにより、アプリケーションの状態管理と画面遷移がシンプルかつ効果的に行われます。
(5)page_a.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; // Blocを使用するためのパッケージ
import 'navigation_bloc.dart'; // NavigationBlocをインポート
import 'navigation_event.dart'; // NavigationEventをインポート
class PageAScreen extends StatelessWidget {
const PageAScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Page A')),
body: Center(
child: ElevatedButton(
onPressed: () {
// Blocを通じて(NavigationBlocインスタンス取得して)
// NavigateToPageBイベントを発行し、
// Page Bに遷移するように指示します。
context.read<NavigationBloc>().add(NavigateToPageB());
},
child: const Text('Go to Page B'),
),
),
);
}
}
(6)page_b.dart
コード説明は上記(5)に準じます。
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'navigation_bloc.dart';
import 'navigation_event.dart';
class PageBScreen extends StatelessWidget {
const PageBScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Page B')),
body: Center(
child: ElevatedButton(
onPressed: () {
context.read<NavigationBloc>().add(NavigateToPageA());
},
child: const Text('Go to Page A'),
),
),
);
}
}
3.総括(処理の流れ)
上記の画面遷移処理は、以下の様な流れで処理されています。
(1) アプリ起動 (main.dart
)
BlocProvider
を使ってNavigationBloc
のインスタンスを作成し、アプリ全体で使用できるように設定します。この設定により、アプリ内のどこからでもNavigationBloc
にアクセスできるようになります。MaterialApp
が表示され、home
プロパティにNavigation
ウィジェットが設定されます。これにより、アプリの初期画面が設定され、アプリのビルドが開始されます。
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
// BlocProviderを使ってNavigationBlocをアプリ全体に提供
<mark style="background-color:#7bdcb5" class="has-inline-color"> return <strong>BlocProvider</strong>(
create: (context) => <strong>NavigationBloc</strong>(),</mark>
// MaterialAppはFlutterアプリの基本的な構造を提供
child: const MaterialApp(
// 最初に表示するウィジェットをNavigationに設定
home: <strong>Navigation</strong>(),
),
);
}
}
(2) 初期画面表示 (main.dart
)
Navigation
クラスが最初に表示されます。BlocBuilder
がNavigationBloc
の状態に基づいてUIを構築します。- 初期状態は
PageA
なので、PageAScreen
が表示されます。
class <strong>Navigation </strong>extends StatelessWidget {
const Navigation({super.key});
@override
Widget build(BuildContext context) {
// BlocBuilderは、NavigationBlocの状態に応じてUIを再構築
return<mark style="background-color:#7bdcb5" class="has-inline-color"> <strong>BlocBuilder</strong><<strong>NavigationBloc</strong>, NavigationState>(</mark>
builder: (context, state) {
<mark style="background-color:#fcb900" class="has-inline-color">if (state is PageA) {</mark>
// stateがPageAのとき、PageAのウィジェットを表示
<mark style="background-color:#fcb900" class="has-inline-color has-black-color">return const PageAScreen();</mark>
} else if (state is PageB) {
// stateがPageBのとき、PageBのウィジェットを表示
return const PageBScreen();
}
// それ以外の状態(初期状態など)はローディングインジケーターを表示
return const Scaffold(body: Center(child: CircularProgressIndicator()));
},
<mark style="background-color:#7bdcb5" class="has-inline-color">);</mark>
}
(3) ページAの表示 (page_a.dart
)
PageAScreen
が表示されます。- ボタンを押すと
NavigateToPageB
イベントが発行されます。
class PageAScreen extends StatelessWidget {
const PageAScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Page A')),
body: Center(
child: ElevatedButton(
onPressed: () {
// Blocを通じて(NavigationBlocインスタンス取得して)
// NavigateToPageBイベントを発行し、
// Page Bに遷移するように指示します。
<mark style="background-color:#7bdcb5" class="has-inline-color">context.read<NavigationBloc>().add(<strong>NavigateToPageB</strong>());</mark>
},
child: const Text('Go to Page B'),
),
),
);
}
}
(4) イベント処理 (navigation_bloc.dart
)
NavigationBloc
が(上記の)NavigateToPageB
イベントを受け取り、状態をPageB
に変更します。
// NavigationBlocクラスは、ナビゲーションイベントとナビゲーション状態を管理するBlocです。
class NavigationBloc extends Bloc<NavigationEvent, NavigationState> {
// コンストラクタで初期状態をPageAに設定します。
<mark style="background-color:#fcb900" class="has-inline-color"><strong>NavigationBloc</strong>() : super(PageA()) {</mark>
// on<NavigateToPageA>メソッドは、NavigateToPageAイベントが発生したときにPageA状態に遷移するようにします。
on<NavigateToPageA>((event, emit) => emit(PageA())); // 状態をPageAに遷移させます。
// on<NavigateToPageB>メソッドは、NavigateToPageBイベントが発生したときにPageB状態に遷移するようにします
<mark style="background-color:#7bdcb5" class="has-inline-color">on<<strong>NavigateToPageB</strong>>((event, emit)</mark> => <mark style="background-color:#8ed1fc" class="has-inline-color">emit(PageB()))</mark>; // 状態をPageBに遷移させます。
<mark style="background-color:#fcb900" class="has-inline-color">}</mark>
}
(5) 状態変更とページBの表示 (main.dart
)
BlocBuilder
が新しい状態(PageB
)を検出し、PageBScreen
を表示します。
class Navigation extends StatelessWidget {
const Navigation({super.key});
@override
Widget build(BuildContext context) {
// BlocBuilderは、NavigationBlocの状態に応じてUIを再構築
return <mark style="background-color:#7bdcb5" class="has-inline-color"><strong>BlocBuilder</strong><<strong>NavigationBloc</strong>, NavigationState>(</mark>
builder: (context, state) {
if (state is PageA) {
// stateがPageAのとき、PageAのウィジェットを表示
return const PageAScreen();
} else <mark style="background-color:#7bdcb5" class="has-inline-color">if (state is PageB) {</mark>
// stateがPageBのとき、PageBのウィジェットを表示
<mark style="background-color:#fcb900" class="has-inline-color">return const PageBScreen();</mark>
}
// それ以外の状態(初期状態など)はローディングインジケーターを表示
return const Scaffold(body: Center(child: CircularProgressIndicator()));
},
<mark style="background-color:#7bdcb5" class="has-inline-color">);</mark>
}
}
(6) ページBの表示 (page_b.dart
)
PageBScreen
が表示されます。- ボタンを押すと
NavigateToPageA
イベントが発行されます。
class <strong><mark style="background-color:#7bdcb5" class="has-inline-color">PageBScreen </mark></strong>extends StatelessWidget {
const PageBScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Page B')),
body: Center(
child: ElevatedButton(
<mark style="background-color:#fcb900" class="has-inline-color">onPressed: () {</mark>
// Blocを通じて(NavigationBlocインスタンス取得して)
// NavigateToPageAイベントを発行し、
// Page Aに遷移するように指示します。
<mark style="background-color:#fcb900" class="has-inline-color">context.read<<strong>NavigationBloc</strong>>().add(<strong>NavigateToPageA</strong>());</mark>
},
child: const Text('Go to Page A'),
),
),
);
}
}
(7) イベント処理 (navigation_bloc.dart
)
NavigationBloc
がNavigateToPageA
イベントを受け取り、状態をPageA
に変更します。
// NavigationBlocクラスは、ナビゲーションイベントとナビゲーション状態を管理するBlocです。
class <strong>NavigationBloc </strong>extends Bloc<NavigationEvent, NavigationState> {
// コンストラクタで初期状態をPageAに設定します。
<mark style="background-color:#7bdcb5" class="has-inline-color"><strong>NavigationBloc</strong>() : super(PageA()) {</mark>
// on<NavigateToPageA>メソッドは、NavigateToPageAイベントが発生したときにPageA状態に遷移するようにします。
<mark style="background-color:#fcb900" class="has-inline-color">on<<strong>NavigateToPageA</strong>>((event, emit) => emit(<strong>PageA</strong>()));</mark> // 状態をPageAに遷移させます。
// on<NavigateToPageB>メソッドは、NavigateToPageBイベントが発生したときにPageB状態に遷移するようにします
on<NavigateToPageB>((event, emit) => emit(PageB())); // 状態をPageBに遷移させます。
<mark style="background-color:#7bdcb5" class="has-inline-color">}</mark>
}
(8) 状態変更とページAの表示 (main.dart
)
BlocBuilder
が新しい状態(PageA
)を検出し、再びPageAScreen
を表示します。
class Navigation extends StatelessWidget {
const Navigation({super.key});
@override
Widget build(BuildContext context) {
// BlocBuilderは、NavigationBlocの状態に応じてUIを再構築
<mark style="background-color:#7bdcb5" class="has-inline-color">return <strong>BlocBuilder</strong><NavigationBloc, NavigationState>(</mark>
builder: (context, state) {
if (state is PageA) {
// stateがPageAのとき、PageAのウィジェットを表示
return const PageAScreen();
} else <mark style="background-color:#fcb900" class="has-inline-color has-black-color">if (state is PageB) {</mark>
// stateがPageBのとき、PageBのウィジェットを表示
<mark style="background-color:#fcb900" class="has-inline-color"> return const PageBScreen();</mark>
}
// それ以外の状態(初期状態など)はローディングインジケーターを表示
return const Scaffold(body: Center(child: CircularProgressIndicator()));
},
<mark style="background-color:#7bdcb5" class="has-inline-color">);</mark>
}
}
この一連の流れにより、flutter_bloc
を使用して状態管理と画面遷移を実現しています。イベントが発行されると、Bloc
がそのイベントを処理し、適切な状態に遷移することで、UIが更新されます。
次回内容> [基礎知識]画面遷移を実現するパッケージ/go_router
go_router は、Googleが開発したパッケージで、シンプルで強力なルーティングシステムを提供し、Flutterアプリケーションの開発を効率的に進めるための優れたツールです。
特にURLベースのナビゲーションを必要とする場合や、Flutter Webとの統合を考慮する場合に非常に有用です。
1.サンプル(動作)
以下は、(前回記事と同じ仕様ですが)2つの画面(PageA画面(page_a.dart)、PageB画面(page_b.dart))を用意し、相互の画面遷移を行うサンプルです。