スキル一覧に戻る
abhishekbrt

flutter-state-management

by abhishekbrt

glow state

0🍴 0📅 2026年1月11日
GitHubで見るManusで実行

SKILL.md


name: flutter-state-management description: State management patterns for Flutter with Riverpod as primary solution. Covers provider types, async state, and local state patterns. Use when managing app state or implementing feature state logic.

Flutter State Management

Quick Decision

ScenarioUse
Form input, toggle, animation triggersetState
Single shared value (simple)ValueNotifier or StateProvider
Feature with loading/error/successAsyncNotifierProvider (Riverpod)
Mutable state with business logicNotifierProvider (Riverpod)
Complex event flows, event trackingBLoC (alternative)

Detailed Guides

TopicGuideUse When
Riverpodriverpod.mdPrimary - most features, DI, async state
BLoC Patternbloc.mdAlternative - complex event-driven flows

Riverpod Quick Start

Setup

// main.dart
import 'package:flutter_riverpod/flutter_riverpod.dart';

void main() {
  runApp(const ProviderScope(child: MyApp()));
}

Provider with Code Generation

// features/counter/presentation/providers/counter_provider.dart
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'counter_provider.g.dart';

@riverpod
class Counter extends _$Counter {
  @override
  int build() => 0;

  void increment() => state++;
  void decrement() => state--;
}

Consuming in Widget

class CounterScreen extends ConsumerWidget {
  const CounterScreen({super.key});

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

    return Scaffold(
      body: Center(child: Text('Count: $count')),
      floatingActionButton: FloatingActionButton(
        onPressed: () => ref.read(counterProvider.notifier).increment(),
        child: const Icon(Icons.add),
      ),
    );
  }
}

Local State

setState (Widget-Local)

class CounterWidget extends StatefulWidget {
  const CounterWidget({super.key});

  @override
  State<CounterWidget> createState() => _CounterWidgetState();
}

class _CounterWidgetState extends State<CounterWidget> {
  int _count = 0;

  void _increment() {
    setState(() => _count++);
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('Count: $_count'),
        ElevatedButton(onPressed: _increment, child: const Text('Add')),
      ],
    );
  }
}

ValueNotifier (Shared Single Value)

// Create notifier
final counterNotifier = ValueNotifier<int>(0);

// Listen in widget
ValueListenableBuilder<int>(
  valueListenable: counterNotifier,
  builder: (context, value, child) {
    return Text('Count: $value');
  },
)

// Update
counterNotifier.value++;

// Dispose when done
counterNotifier.dispose();

Async State Pattern

AsyncNotifier for Feature State

@riverpod
class UserProfile extends _$UserProfile {
  @override
  FutureOr<User?> build() async {
    final repo = ref.watch(userRepositoryProvider);
    return repo.getCurrentUser();
  }

  Future<void> updateName(String name) async {
    final current = state.valueOrNull;
    if (current == null) return;

    state = const AsyncLoading();
    state = await AsyncValue.guard(() async {
      final repo = ref.read(userRepositoryProvider);
      return repo.updateUser(current.copyWith(name: name));
    });
  }
}

Consuming AsyncValue

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

  return userAsync.when(
    data: (user) => user != null 
        ? ProfileView(user: user)
        : const LoginPrompt(),
    loading: () => const ProfileSkeleton(),
    error: (error, _) => ErrorView(
      message: error.toString(),
      onRetry: () => ref.invalidate(userProfileProvider),
    ),
  );
}

ref.watch vs ref.read

MethodRebuildsUse In
ref.watch()Yesbuild() method
ref.read()NoCallbacks, event handlers
ref.listen()No (triggers callback)Side effects
@override
Widget build(BuildContext context, WidgetRef ref) {
  // Side effects (snackbar, navigation)
  ref.listen(authProvider, (prev, next) {
    if (next is AsyncError) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text(next.error.toString())),
      );
    }
  });

  // Reactive rebuild
  final auth = ref.watch(authProvider);
  
  return ElevatedButton(
    // Non-reactive callback
    onPressed: () => ref.read(authProvider.notifier).logout(),
    child: const Text('Logout'),
  );
}

When to Use BLoC

Use BLoC (see bloc.md) when you need:

ScenarioWhy BLoC
Event logging/analyticsEvents create audit trail
Complex event transformationson<Event> handlers with debounce, throttle
Undo/redo functionalityEvent replay capability
Strict separation of concernsEvents as explicit API

For most features, Riverpod's NotifierProvider or AsyncNotifierProvider is simpler and sufficient.

Provider Types Summary

TypeUse CaseExample
@riverpod functionComputed/derived valuesthemeModeProvider
NotifierProviderMutable sync statecounterProvider
AsyncNotifierProviderMutable async stateauthProvider
FutureProviderRead-only async dataproductsProvider
StreamProviderReal-time datamessagesProvider
Family (parameterized)Per-ID datauserProvider(userId: '123')

Anti-Patterns

AvoidInstead
Business logic in widgetsMove to Notifier classes
ref.read in build()Use ref.watch for reactive updates
Manual loading/error booleansUse AsyncValue from AsyncNotifierProvider
Global mutable stateUse scoped providers with Riverpod
setState after async without mounted checkUse Riverpod (handles lifecycle)
Nested ProviderScope (usually)Single root scope, use overrides for testing

スコア

総合スコア

50/100

リポジトリの品質指標に基づく評価

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

0/10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

3ヶ月以内に更新がある

0/10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

0/5

レビュー

💬

レビュー機能は近日公開予定です