見出し画像

【アプリ開発】FlutterとRiverpodを用いた、外部サイト遷移処理中のローディング状態処理とボタン非活性化

はじめに

Flutterアプリから外部サイトへ遷移させたい場合で、遷移まで時間がかかる(APIリクエストしてresponseの情報を元に遷移するなど)際の処理として

  • ローディングアイコンを表示して、遷移まではボタンを非活性化する

  • 遷移するまでの間にユーザーが画面を移動しないようにする

などの制御ニーズが発生する。

Flutter と Riverpod を利用してローディングの状態管理を行い、ボタンの非活性化と、ナビゲーションの戻るボタンの無効化を行ったので、備忘録記載。



コード全体像

import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:url_launcher/url_launcher.dart';

final loadingProvider = StateProvider<bool>((ref) => false);

class DataRetrievalPage extends HookConsumerWidget {
  const DataRetrievalPage({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final isLoading = ref.watch(loadingProvider);

    return WillPopScope(
      onWillPop: () async => !isLoading,
      child: Scaffold(
        appBar: AppBar(
          title: const Text('テスト画面'),
          leading: isLoading
              ? null
              : IconButton(
                  icon: const Icon(Icons.arrow_back),
                  onPressed: () => Navigator.of(context).pop(),
                ),
        ),
        body: const Padding(
          padding: EdgeInsets.all(16.0),
          child: _Body(),
        ),
      ),
    );
  }
}

class _Body extends ConsumerWidget {
  const _Body();

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final isLoading = ref.watch(loadingProvider);

    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        const Text(
          '外部サイトに遷移します',
          style: TextStyle(
            fontSize: 16,
            height: 1.5,
          ),
        ),
        const SizedBox(height: 24),
        Center(
          child: Stack(
            alignment: Alignment.center,
            children: [
              ElevatedButton(
                onPressed: isLoading ? null : () async {
									  ref.read(loadingProvider.notifier).state = true;
									  try {
									    final urlString = await fetchExternalUrl();
									    final url = Uri.parse(urlString);
									    
									    if (await canLaunchUrl(url)) {
									      await launchUrl(url);
									    } else {
									      throw Exception('Could not launch $url');
									    }
									  } catch (e) {
									    ref.watch(exceptionHelperProvider).showMessage(e);
									  } finally {
									    ref.read(loadingProvider.notifier).state = false;
									  }
									},
									child: const Text('外部サイトに遷移'),
              ),
              if (isLoading)
                const Positioned(
                  child: CircularProgressIndicator(),
                ),
            ],
          ),
        ),
      ],
    );
  }
}

ローディング状態の管理

loadingProvider という StateProvider を使用してローディングの状態を管理。

続きは、こちらで記載しています。


この記事が参加している募集

よろしければサポートお願いします!いただいたサポートはクリエイターとしての活動費に使わせていただきます!