ספקים שעוקבים אחר ספקים
חלק מהיחידה ניהול מצב במסלול ה-Flutter של Coddy. שיעור 20 מתוך 27.
חלק מהמצב נובע ממצב אחר: סך הדקות הוא סכום האימונים. אפשר לחבר אותן בכל רכיב שמציג סך הכול, או לכתוב זאת פעם אחת, כספק שעוקב אחר ספק אחר:
final totalMinutesProvider = Provider((ref) {
final workouts = ref.watch(workoutsProvider);
return workouts.fold(0, (sum, w) => sum + w.minutes);
});הפונקציה של ספק מקבלת גם ref. כשמשתמשים בתוכה ב-ref.watch, Riverpod זוכר שסך הכול תלוי באימונים. הוסיפו אימון, וסך הכול מחושב מחדש, וכל רכיב שעוקב אחר הסכום נבנה מחדש.
השתמשו כאן ב-ref.watch, ולא ב-ref.read. הפונקציה read לוקחת את הרשימה פעם אחת, כשסך הכול מחושב לראשונה, ואחרי כן הסכום לעולם לא משתנה.
זהו רעיון ה-getter שהוזכר קודם, עבור האפליקציה כולה: שמרו כמה שפחות, ואת כל השאר חשבו על סמך הנתונים ששמרתם.
אתגר
בינוניהסרגל העליון עוקב אחרי totalMinutesProvider, אבל הספק הזה תמיד מחזיר 0.
- גרמו לו לעקוב אחרי
workoutsProviderולהחזיר את הסכום של ערכיminutesבכל האימונים.
נסו בעצמכם
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class Workout {
Workout(this.name, this.minutes);
final String name;
final int minutes;
}
class WorkoutLog extends Notifier<List<Workout>> {
@override
List<Workout> build() => [];
void add(Workout workout) {
state = [...state, workout];
}
}
final workoutsProvider = NotifierProvider<WorkoutLog, List<Workout>>(WorkoutLog.new);
final totalMinutesProvider = Provider((ref) {
return 0;
});
void main() {
runApp(const ProviderScope(child: MyApp()));
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: LogPage(),
);
}
}
class LogPage extends ConsumerWidget {
const LogPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final workouts = ref.watch(workoutsProvider);
final total = ref.watch(totalMinutesProvider);
return Scaffold(
appBar: AppBar(title: Text('Total: $total min')),
body: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () => ref.read(workoutsProvider.notifier).add(Workout('Run', 30)),
child: const Text('Run'),
),
const SizedBox(width: 16),
ElevatedButton(
onPressed: () => ref.read(workoutsProvider.notifier).add(Workout('Yoga', 20)),
child: const Text('Yoga'),
),
],
),
Expanded(
child: ListView(
children: [
for (final w in workouts) ListTile(title: Text(w.name), trailing: Text('${w.minutes} min')),
],
),
),
],
),
);
}
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה ניהול מצב
1העלאת מצב למעלה
העברת מצב לרכיב ילדהעברת פונקציית Callback לרכיב ילדפונקציות Callback ששולחות ערךהעלאת מצב למעלה4Riverpod
Riverpod: ProviderScopeConsumerWidget ו־ref.watchRiverpod: StateProviderRiverpod: Notifierספקים שעוקבים אחר ספקיםבחירת המקום שבו המצב נשמרתרגלו בעצמכם: קומפיילר Flutter אונליין