初めてFlutterを触ってみた- Flutter × Laravel 認証- ④
初めてFlutterを触ってみた④
過去の記事の続きになります。
- 初めてFlutterを触ってみた- Flutter × Laravel 認証- ①
- 初めてFlutterを触ってみた- Flutter × Laravel 認証- ②
- 初めてFlutterを触ってみた- Flutter × Laravel 認証- ③
この記事では、Flutterの画面遷移や、これまで触れてこなかったFlutterライブラリについて、ゆるく解説します。
Flutterのスタックについて
Flutterの画面遷移は「スタック」を使って管理され、操作は主に以下の3つに分類されます:
1. Push操作
新しい画面をスタックに積み上げる。
初期スタック状態:
+-----------+
| 画面A | ← 現在表示中の画面
+-----------+
↓ Push操作
実行後スタック状態:
+-----------+
| 画面B | ← 新しく追加された画面
+-----------+
| 画面A | ← スタックに残る
+-----------+
2. Pop操作
現在の画面を取り除き、前の画面に戻る。
初期スタック状態:
+-----------+
| 画面B | ← 現在表示中の画面
+-----------+
| 画面A |
+-----------+
↓ Pop操作
実行後スタック状態:
+-----------+
| 画面A | ← 表示される画面に戻る
+-----------+
3. Replace操作
現在の画面を新しい画面に置き換える。
初期スタック状態:
+-----------+
| 画面A | ← 現在表示中の画面
+-----------+
↓ Replace操作
実行後スタック状態:
+-----------+
| 画面C | ← 置き換え後の画面
+-----------+
画面遷移の方法
Navigator.push()
1. - 分類: Push操作
- 解説: 現在の画面の上に新しい画面を積み上げる。元の画面はスタックに保持されます。
-
使用例:
Navigator.push( context, MaterialPageRoute(builder: (context) => NewScreen()), );
-
図解:
初期スタック状態: +-----------+ | Dashboard | ← 現在表示中の画面 +-----------+ | Home | +-----------+ ↓ Navigator.push( context, MaterialPageRoute(builder: (context) => NewScreen()), ); 実行後スタック状態: +-----------+ | NewScreen | ← 現在表示中の画面 +-----------+ | Dashboard | +-----------+ | Home | +-----------+
- ケース: 詳細画面への遷移や、ウィザード形式の次のステップに進む場合など。
Navigator.pop()
2. - 分類: Pop操作
- 解説: 現在の画面を取り除き、1つ前の画面に戻る。取り除いた画面はスタックから削除されます。
-
使用例
Navigator.pop(context);
-
図解
初期スタック状態: +-----------+ | NewScreen | ← 現在表示中の画面 +-----------+ | Dashboard | +-----------+ | Home | +-----------+ ↓ Navigator.pop(context); 実行後スタック状態: +-----------+ | Dashboard | ← 現在表示中の画面 +-----------+ | Home | +-----------+
- ケース:詳細画面からリスト画面に戻るときなど。
Navigator.pushReplacement()
3. - 分類: Replace操作
- 解説: 現在の画面を削除し、新しい画面に置き換える。元の画面はスタックに残りません。
-
使用例:
Navigator.pushReplacement( context, MaterialPageRoute(builder: (context) => NewScreen()), );
-
図解
初期スタック状態: +-----------+ | Dashboard | ← 現在表示中の画面 +-----------+ | Home | +-----------+ ↓ Navigator.pushReplacement( context, MaterialPageRoute(builder: (context) => NewScreen()), ); 実行後スタック状態: +-----------+ | NewScreen | ← 現在表示中の画面 +-----------+ | Home | +-----------+
- ケース:認証が完了した後、ホーム画面に移動する際など。
Navigator.pushAndRemoveUntil()
4. - 分類: Push操作 + 条件付きPop操作
- 解説: 新しい画面を積み上げると同時に、指定条件に合うまでスタック内の画面を削除します。
-
使用例:
Navigator.pushAndRemoveUntil( context, MaterialPageRoute(builder: (context) => NewScreen()), (Route<dynamic> route) => route.isFirst, );
-
図解:
初期スタック状態: +-----------+ | Dashboard | ← 現在表示中の画面 +-----------+ | Login | +-----------+ | Home | +-----------+ ↓ Navigator.pushAndRemoveUntil( context, MaterialPageRoute(builder: (context) => NewScreen()), (Route<dynamic> route) => route.isFirst, ); 実行後スタック状態: +-----------+ | NewScreen | ← 現在表示中の画面 +-----------+ | Home | +-----------+
- ケース: ユーザーがログインした後、ログイン画面に戻れないようにする場合。
Navigator.popUntil()
5. - 分類: 条件付きPop操作
- 解説: 条件に合うまでスタックから画面を取り除き、指定の画面に戻ります。
-
使用例:
Navigator.popUntil(context, (route) => route.isFirst);
-
図解:
初期スタック状態: +-----------+ | Settings | ← 現在表示中の画面 +-----------+ | Dashboard | +-----------+ | Login | +-----------+ | Home | +-----------+ ↓ Navigator.popUntil(context, (route) => route.isFirst); 実行後スタック状態: +-----------+ | Home | ← 現在表示中の画面 +-----------+
- ケース: 複数の画面を経由してホーム画面に戻る場合。
Navigator.pushNamed()
6. - 分類: Push操作
- 解説: ルート名を使用して、新しい画面をスタックに積み上げます。
-
使用例:
Navigator.pushNamed(context, '/newScreen');
-
図解:
初期スタック状態: +-----------+ | Dashboard | ← 現在表示中の画面 +-----------+ | Home | +-----------+ ↓ Navigator.pushNamed(context, '/newScreen'); 実行後スタック状態: +-----------+ | NewScreen | ← 現在表示中の画面 +-----------+ | Dashboard | +-----------+ | Home | +-----------+
- ケース: 画面遷移の際に名前付きルートを使いたい場合。
Navigator.pushNamedAndRemoveUntil()
7. - 分類: Push操作 + 条件付きのPop操作
- 解説: ルート名を使って新しい画面を追加し、条件に合うまでスタックを削除します。
-
使用例:
Navigator.pushNamedAndRemoveUntil(context, '/newScreen', (route) => false);
-
図解:
初期スタック状態: +-----------+ | Settings | ← 現在表示中の画面 +-----------+ | Dashboard | +-----------+ | Login | +-----------+ | Home | +-----------+ ↓ Navigator.pushNamedAndRemoveUntil(context, '/newScreen', (route) => false); 実行後スタック状態: +-----------+ | NewScreen | ← 現在表示中の画面 +-----------+
- ケース: ナビゲーションをリセットして新しい画面に遷移する場合。
試してみたFlutterライブラリ
flutter_dotenv
1. flutter_dotenv
は、.env
ファイルを使って環境変数をFlutterアプリに読み込むライブラリです。APIキーやURLなど、アプリにハードコーディングしないほうが良い情報を管理するのに役立ちます。
以下は、flutter_dotenv
を使用してAPIのエンドポイントURLを設定する例です。
1.1 .envファイルの設定
.env
ファイルをプロジェクトのルートディレクトリに作成し、APIのベースURLを定義します。
これは、ハードコーディングを避け、環境ごとに異なる設定を簡単に切り替えるためです。
BASE_API_URL='http://localhost/api'
1.2 dotenvパッケージの読み込み
アプリの起動時に.env
ファイルを読み込むために、main.dart
でdotenv.load()
を呼び出します。
void main() async {
// .envファイルの読み込み
await dotenv.load(fileName: ".env");
runApp(ChangeNotifierProvider(
create: (conntext) => AuthProvider(),
child: const MyApp(),
));
}
1.3 環境変数を定数として定義
.env
ファイルから読み込んだ環境変数をAppConstクラス
に定義してアプリ内で使用します。
例えば、APIリクエストの際にベースURLとして定義するコードです。
import 'package:flutter_dotenv/flutter_dotenv.dart';
class AppConst {
static final String baseApiUrl = dotenv.env['BASE_API_URL'] ?? 'http://localhost:3000/api';
...
}
上記のコードでは、dotenv.env['BASE_API_URL']
で.env
ファイルのBASE_API_URL
を取得します。
もし取得できなかった場合には、デフォルトのURL(例:http://localhost:3000/api)を使うようにしています。
1.4 実際の使用例
例えば、ServiceUtilsクラス
でAPIのベースURLを使ってリクエストを行うときに利用できます。
import 'package:flutter_sample/src/constants/app_const.dart';
import 'package:http/http.dart' as http;
...
class ServiceUtils {
static final String baseApiUrl = AppConst.baseApiUrl;
...
// POSTリクエスト
static postRequest(String uri, String? token, String? data) async {
final url = Uri.parse('$baseApiUrl/$uri');
final headers = getHeaders(token);
final response = await http.post(
url,
headers: headers,
body: data,
);
return response;
}
}
shimmer
2. shimmer
は、読み込み中のプレースホルダーとして、光の反射のようなアニメーションを表示するために使われます。
データがロードされるまでの間に、ユーザー体験を向上させることができます。
今回は、HomeScreen
でデータを取得するまでの間、ローディングプレースホルダーとしてshimmer
を使用しました。以下は具体的な実装例です。
2.1 HomeScreenのローディング時にshimmerを使用
データがまだロード中の場合は、ListViewで複数のHomeCard
プレースホルダーを表示します。
データがロードされたら、実際のデータを表示するようにしています。
Widget build(BuildContext context) {
// AuthProviderの状態に応じた処理
return Scaffold(
body: _isLoading
? ListView.builder(
itemCount: 4,
itemBuilder: (context, index) => const HomeCard(), // shimmer使用Widget
) : ListView.builder(
controller: _scrollController,
itemCount: data.length + (_isLoadingMore ? 1 : 0), // ローディング中のインジケーター用に1アイテム追加
itemBuilder: (context, index) {
if (index < data.length) {
// 通常のデータをリスト表示
return _buildHomeCard(
data[index].image,
data[index].title,
data[index].id
);
} else {
// 追加データ取得中のローディングインジケーター
return const Padding(
padding: EdgeInsets.all(8.0),
child: Center(child: CircularProgressIndicator()),
);
}
},
),
);
}
2.2 HomeCardウィジェットでshimmerアニメーションの表示
HomeCardウィジェット
では、Shimmer.fromColors
を使用して、プレースホルダーに光の反射アニメーションを追加しています。
baseColor
とhighlightColor
を指定することで、煌めきの色味や強調効果を調整することが可能です。
Widget _buildShimmerEffect() {
return Shimmer.fromColors(
baseColor: Colors.grey.withOpacity(0.2), // 煌めきの色
highlightColor: Colors.white,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
width: width, // お好みのサイズ
height: height, // お好みのサイズ
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8.0),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 1,
blurRadius: 7,
offset: const Offset(0, 3),
),
]
),
child: const Center(
child: Text(
'Loading...',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
),
),
),
);
}
flutter_html
3. flutter_html
は、Flutterウィジェット内でHTMLコンテンツをレンダリングするためのライブラリです。
今回の例では、データベースに保存されたアイテムの説明文にHTMLタグを含めておき、HomeDetailScreen
でその説明文を表示する際にflutter_html
を使用しました。
説明文に含まれるスタイル(例:文字色の変更)が正しく反映されるかを試しています。
3.1 Laravelでのデータ準備
Laravel側では、ItemTableSeeder
を使用してアイテム情報をデータベースに保存しました。
説明文には、HTMLタグ(例:<div style='color: red;'>)を含め、レンダリング時にそのスタイルが反映されるようにしています。
namespace Database\Seeders;
use Carbon\Carbon;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
class ItemTableSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
for ($i = 1; $i <= 10; $i++) {
DB::table('items')->upsert([
'id' => $i,
'title' => "Item {$i}",
'image' => "https://placehold.jp/ff006f/ffffff/150x150.png?text=Item{$i}",
'description' => "Description of Item {$i}\n
<div style='color: red;'>This is a sample description.</div>\n", //説明文
'created_at' => Carbon::now(),
'updated_at' => Carbon::now(),
], [
'id'
], [
'title',
'image',
'description',
'created_at',
'updated_at',
]);
}
}
}
3.2 FlutterでのHTML表示
HomeDetailScreen
で、サーバーから取得したアイテムデータをflutter_html
ライブラリを使って表示します。
Htmlウィジェット
を利用することで、説明文に含まれるHTMLタグが解釈され、スタイルが適用されます。
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: _isLoading
? const CircularProgressIndicator()
: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.network(_data.image),
const SizedBox(height: 20),
Text(
_data.title,
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
Html(
data: _data.description, // HTML形式の説明文を表示
),
],
),
),
);
}
まとめ
今回は、簡単にですが、画面遷移の方法と触れられてなかったライブラリの話をしました。
また何か思いついたら、この記事を更新していこうと思います。
Flutterに詳しい方や初心者の方からのアドバイスも大歓迎ですので、ぜひコメントいただけると嬉しいです!
Discussion