FlutterFlutter · Chapterبەش 11 · Lectureوانە 11.5

FutureProvider and StreamProviderFutureProvider و StreamProvider

The lecture's to-do app in three steps: injecting the signed-in User with Provider (dependency injection) while a FutureBuilder loads the list, replacing the FutureBuilder with a FutureProvider (initialData, loading and catchError), and showing live data with a StreamProvider.

ئەرکەکانی وانەکە لە سێ هەنگاودا: تێخستنی چووە ژوورەوە بە Provider () لە کاتێکدا FutureBuilderـێک لیستەکە بار دەکات، گۆڕینەوەی FutureBuilder بە FutureProvider (initialData، بارکردن و catchError) و پیشاندانی دراوەی زیندوو بە StreamProvider.

12 min read11 خولەک خوێندنەوە · 9 stepsهەنگاو

What you will learnچی فێر دەبیت

  • Inject a value that already exists with Provider.value and explain dependency injection in the to-do app.
  • Replace a FutureBuilder with a FutureProvider and read the result with context.watch.
  • Use initialData, a nullable type and catchError for loading and errors.
  • Show live data from a Stream with a StreamProvider.
  • Choose between FutureBuilder, FutureProvider, StreamProvider and ChangeNotifierProvider.
  • نرخێک کە هەیە بە Provider.value تێبخەیت و لە ئەرکەکاندا ڕوون بکەیتەوە.
  • FutureBuilderـێک بە FutureProvider بگۆڕیتەوە و ئەنجامەکە بە context.watch بخوێنیتەوە.
  • initialData، جۆرێکی nullable و catchError بۆ بارکردن و بەکار بهێنیت.
  • دراوەی زیندوو لە بە StreamProvider پیشان بدەیت.
  • لە نێوان FutureBuilder، FutureProvider، StreamProvider و ChangeNotifierProviderـدا هەڵبژێریت.

Providers for data that arrives laterprovider بۆ دراوەیەک کە دواتر دەگات

Much of an app's data is not ready at once: a user loaded after sign-in, a to-do list from a server, live updates from a database. Dart has two types for that, which you met in Futures and async: a Future gives one value later, and a Stream gives many values over time. On a screen you showed them with FutureBuilder and StreamBuilder.

زۆربەی دراوەی یەکسەر ئامادە نییە: کە دوای بار دەکرێت، لیستی ئەرکەکان لە ، نوێکارییە زیندووەکان لە . Dart دوو جۆری بۆ ئەمە هەیە کە لە نرخەکانی داهاتوو و async بینیت: یەک نرخ دواتر دەدات و بە درێژایی کات چەندین نرخ دەدات. لەسەر شاشە بە FutureBuilder و StreamBuilder پیشانت دان.

The provider package has two providers for exactly these types. FutureProvider waits for a Future and gives its result to the widgets below; StreamProvider listens to a Stream and always gives the latest value. The lecture shows both in its to-do app, step by step.

provider دوو providerـی ڕێک بۆ ئەم جۆرانە هەیە. FutureProvider چاوەڕێی دەکات و ئەنجامەکەی دەدات بە ئەو لە خوارەوەن؛ StreamProvider گوێ لە دەگرێت و هەمیشە دوایین نرخ دەدات. وانەکە هەردووکیان لە ئەرکەکانیدا هەنگاو بە هەنگاو پیشان دەدات.

Quick checkپشکنینی خێراA screen must show the phone's battery level, which changes every few minutes. Which Dart type fits the data?شاشەیەک دەبێت ئاستی باتریی مۆبایلەکە پیشان بدات، کە هەر چەند خولەکێک دەگۆڕێت. کام جۆری Dart بۆ دراوەکە گونجاوە؟

The lecture's to-do app: injecting the Userئەپی ئەرکەکانی وانەکە: تێخستنی User

The lecture starts from a to-do list whose app bar shows the signed-in user. Two services fetch the data: UserDataService.getUser(id) and TodoDataService.getTodoList(). The lecture keeps them in a service locator, one central place where the app finds its services. (A later lesson, Organising an MVVM Project: Folders, Services and get_it, builds a real service locator with the get_it package.) Here are the two services, returning fake data after a short wait, like a slow server:

وانەکە لە لیستێکی ئەرکەکانەوە دەست پێ دەکات کە ئەو پیشان دەدات کە چووەتە ژوورەوە. دوو دراوەکان دەهێنن: UserDataService.getUser(id) و TodoDataService.getTodoList(). وانەکە ئەمانە لە هەڵدەگرێت، یەک شوێنی ناوەندی کە تێدا دەدۆزێتەوە. (وانەیەکی دواتر، ڕێکخستنی پڕۆژەیەکی MVVM: فۆڵدەرەکان، خزمەتگوزارییەکان و ⁦get_it⁩، بە ⁦get_it⁩ ڕاستەقینە دروست دەکات.) ئەمانە دوو ، کە دوای چاوەڕوانییەکی کورت دراوەی ساختە دەگەڕێننەوە، وەک خاو:

The services (todo_services.dart)خزمەتگوزارییەکان ⁦(todo_services.dart)⁩todo_services.dartDART
class User {
  const User({required this.id, required this.name});

  final int id;
  final String name;
}

class Todo {
  const Todo({required this.id, required this.title});

  final int id;
  final String title;
}

// The lecture's "service locator" holds services like these.
// They pretend to be a slow server: the data arrives later.
class UserDataService {
  Future<User> getUser(int id) async {
    await Future.delayed(const Duration(milliseconds: 500));
    return User(id: id, name: 'Shilan');
  }
}

class TodoDataService {
  // One answer, later: a Future.
  Future<List<Todo>> getTodoList() async {
    await Future.delayed(const Duration(seconds: 1));
    return const [
      Todo(id: 1, title: 'Read chapter 8'),
      Todo(id: 2, title: 'Revise for the quiz'),
      Todo(id: 3, title: 'Buy a notebook'),
    ];
  }

  // Many answers over time: a Stream. Each value is the whole new list.
  Stream<List<Todo>> watchTodoList() async* {
    var todos = <Todo>[];
    for (final title in ['Read chapter 8', 'Call Hana', 'Practise layouts']) {
      await Future.delayed(const Duration(seconds: 1));
      todos = [...todos, Todo(id: todos.length + 1, title: title)];
      yield todos;
    }
  }
}

In the lecture's first version, only the User uses Provider: it is fetched once at sign-in and injected above MaterialApp, so every screen can read it without creating it (that is dependency injection). The to-do list still uses a stateful widget and a FutureBuilder:

لە وەشانی یەکەمی وانەکەدا، تەنها ⁦User Provider⁩ بەکار دەهێنێت: یەک جار لە کاتی دەهێنرێت و لە سەرووی MaterialAppـەوە تێدەخرێت، بۆیە هەموو شاشەیەک دەتوانێت بیخوێنێتەوە بەبێ ئەوەی دروستی بکات (ئەمە پێی دەوترێت تێخستنی وابەستەیی). لیستی ئەرکەکان هێشتا و FutureBuilder بەکار دەهێنێت:

Step 1: User injected, list with a FutureBuilderهەنگاوی ١: User تێخراوە، لیست بە FutureBuilderDARTRuns on a phone (or an emulator)لەسەر مۆبایل کار دەکات
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

import 'todo_services.dart';

Future<void> main() async {
  final user = await UserDataService().getUser(1); // sign-in finished
  runApp(buildApp(user));
}

Widget buildApp(User user) {
  // Only the User goes through Provider (dependency injection).
  return Provider<User>.value(
    value: user, // the object already exists, so .value is right
    child: const MaterialApp(home: TodoListScreen()),
  );
}

// The to-do list still uses a stateful widget and a FutureBuilder.
class TodoListScreen extends StatefulWidget {
  const TodoListScreen({super.key});

  @override
  State<TodoListScreen> createState() => _TodoListScreenState();
}

class _TodoListScreenState extends State<TodoListScreen> {
  late final Future<List<Todo>> _todos = TodoDataService().getTodoList();

  @override
  Widget build(BuildContext context) {
    final user = Provider.of<User>(context); // injected, not created here
    return Scaffold(
      appBar: AppBar(title: Text("${user.name}'s to-dos")),
      body: FutureBuilder<List<Todo>>(
        future: _todos,
        builder: (context, snapshot) {
          if (snapshot.connectionState != ConnectionState.done) {
            return const Center(child: CircularProgressIndicator());
          }
          if (snapshot.hasError) {
            return Center(child: Text('Error: ${snapshot.error}'));
          }
          final todos = snapshot.data!;
          return ListView.builder(
            itemCount: todos.length,
            itemBuilder: (context, i) => ListTile(title: Text(todos[i].title)),
          );
        },
      ),
    );
  }
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 7

The user is loaded before the app starts, so it already exists…

پێش دەستپێکردنی بار دەکرێت، بۆیە پێشتر هەیە…

Lineدێڕی 14

…and .value is the right constructor for an existing object.

…و .value دروستە بۆ کە هەیە.

Lineدێڕی 28

The Future is made once, in the State object, not in build.

یەک جار دروست دەکرێت، لە ، نەک لە buildـدا.

Lineدێڕی 32

The screen receives the user; it does not create it.

شاشەکە وەردەگرێت؛ دروستی ناکات.

Quick checkپشکنینی خێراIn this first version, which data goes through Provider?لەم وەشانە یەکەمەدا، کام دراوە بە Providerـدا تێدەپەڕێت؟

FutureProvider: no more FutureBuilderFutureProvider: چیتر FutureBuilder نییە

In the next step, the to-do list moves into a provider too. A FutureProvider takes a function that creates the Future, waits for it, and makes the result available to the widgets below. The screen no longer needs a State object or a FutureBuilder: it simply watches a List<Todo>.

لە هەنگاوی داهاتوودا، لیستی ئەرکەکانیش دەچێتە ناو providerـێکەوە. FutureProvider وەردەگرێت کە دروست دەکات، چاوەڕێی دەکات و ئەنجامەکەی بەردەست دەکات بۆ ئەو لە خوارەوەن. شاشەکە چیتر پێویستی بە یان FutureBuilder نییە: بە سادەیی List<Todo>ـێک watch دەکات.

Step 2: the list from a FutureProviderهەنگاوی ٢: لیستەکە لە FutureProviderـێکەوەDARTRuns on a phone (or an emulator)لەسەر مۆبایل کار دەکات
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

import 'todo_services.dart';

Future<void> main() async {
  final user = await UserDataService().getUser(1);
  runApp(buildApp(user));
}

Widget buildApp(User user) {
  return MultiProvider(
    providers: [
      Provider<User>.value(value: user),
      FutureProvider<List<Todo>>(
        create: (_) => TodoDataService().getTodoList(),
        initialData: const [], // shown until the Future completes
      ),
    ],
    child: const MaterialApp(home: TodoListScreen()),
  );
}

// No State object and no FutureBuilder any more.
class TodoListScreen extends StatelessWidget {
  const TodoListScreen({super.key});

  @override
  Widget build(BuildContext context) {
    final user = context.watch<User>();
    final todos = context.watch<List<Todo>>(); // [] first, then the list
    return Scaffold(
      appBar: AppBar(title: Text("${user.name}'s to-dos")),
      body: ListView.builder(
        itemCount: todos.length,
        itemBuilder: (context, i) => ListTile(title: Text(todos[i].title)),
      ),
    );
  }
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 16

The provider calls the service once and listens to the Future.

providerـەکە یەک جار بانگی دەکات و گوێ لە دەگرێت.

Lineدێڕی 17

Required: the value the screen gets before the Future completes.

پێویستە: ئەو نرخەی شاشەکە پێش تەواوبوونی وەری دەگرێت.

Lineدێڕی 31

First [], then, one second later, the three to-dos: the screen is rebuilt once.

سەرەتا []، پاشان، چرکەیەک دواتر، سێ ئەرکەکە: شاشەکە یەک جار دووبارە دروست دەکرێتەوە.

Quick checkپشکنینی خێراWhat does a widget below FutureProvider<List<Todo>>(initialData: const [], …) see in the first frame? لە خوارەوەی FutureProvider<List<Todo>>(initialData: const [], …) لە یەکەم کێشانی شاشەکەدا چی دەبینێت؟

FutureProvider: no more FutureBuilderFutureProvider: چیتر FutureBuilder نییە

Loading and errors with FutureProviderچاوەڕوانی و هەڵەکان لەگەڵ FutureProvider

An empty list as initialData hides the difference between “still loading” and “there are no to-dos”. Give the provider a nullable type and initialData: null: then null means loading. A FutureBuilder had an error branch; FutureProvider has catchError, a callback that turns an error into a value. Without it, a failing Future is reported as an error in your app:

لیستێکی بەتاڵ وەک initialData جیاوازیی نێوان «هێشتا بار دەکرێت» و «هیچ ئەرکێک نییە» دەشارێتەوە. جۆرێکی nullable و initialData: null بدە بە providerـەکە: ئەو کاتە null واتە هێشتا بار دەکرێت. FutureBuilder لقێکی هەبوو؛ FutureProvider catchErrorـی هەیە، کە دەگۆڕێت بۆ نرخێک. بەبێ ئەو، شکستخواردوو وەک لە ڕادەگەیەنرێت:

A nullable type for “loading”, catchError for failuresجۆرێکی nullable بۆ «بارکردن»، catchError بۆ شکستەکانDARTRuns on a phone (or an emulator)لەسەر مۆبایل کار دەکات
Widget buildApp() {
  return FutureProvider<List<Todo>?>(
    create: (_) => TodoDataService().getTodoList(),
    initialData: null, // null means "still loading"
    catchError: (_, error) => const [], // the Future failed: use an empty list
    child: const MaterialApp(home: TodoListScreen()),
  );
}
DARTRuns on a phone (or an emulator)لەسەر مۆبایل کار دەکات
class TodoListScreen extends StatelessWidget {
  const TodoListScreen({super.key});

  @override
  Widget build(BuildContext context) {
    final todos = context.watch<List<Todo>?>(); // the same nullable type
    Widget body;
    if (todos == null) {
      body = const Center(child: CircularProgressIndicator()); // loading
    } else if (todos.isEmpty) {
      body = const Center(child: Text('No to-dos.'));
    } else {
      body = ListView(
        children: [for (final t in todos) ListTile(title: Text(t.title))],
      );
    }
    return Scaffold(appBar: AppBar(title: const Text('My to-dos')), body: body);
  }
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 6

Read with exactly the same type, List<Todo>?, or the provider is not found.

ڕێک بە هەمان جۆر بیخوێنەرەوە، List<Todo>?، ئەگەرنا providerـەکە نادۆزرێتەوە.

PointخاڵFutureBuilderFutureProvider
Where it livesلە کوێ دەژیInside one screen's buildلەناو buildـی یەک شاشەداAbove the screens, in the treeلە سەرووی شاشەکانەوە، لە
Who can use the resultکێ دەتوانێت ئەنجامەکە بەکار بهێنێتOnly its builderتەنها builderـەکەیEvery widget below, by typeهەموو خوارەوە، بەپێی جۆر
Waitingچاوەڕوانیsnapshot.connectionStateinitialData (for example null)initialData (بۆ نموونە null)
Errorssnapshot.hasErrorcatchError
Good forباشە بۆOne screen, full control of every stateیەک شاشە، کۆنترۆڵی تەواوی هەموو دۆخێکData many screens shareدراوەیەک کە چەند شاشەیەک هاوبەشی دەکەن
Quick checkپشکنینی خێراYour FutureProvider<User?> has initialData: null. A widget writes context.watch<User>(). What happens?FutureProvider<User?>ـەکەت initialData: nullـی هەیە. context.watch<User>() دەنووسێت. چی ڕوو دەدات؟

StreamProvider: live dataStreamProvider: دراوەی زیندوو

The lecture's last tree is the same as the FutureProvider one, with one change: the data service returns a Stream<List<Todo>>, and a StreamProvider sits in the tree. Each time the stream sends a new list, the provider exposes it and the screen is rebuilt. Typical sources are a Firestore query that sends a fresh list on every change (see Reading live with StreamBuilder), the battery level, or the sign-in state.

دوایین وانەکە وەک هی FutureProviderـە، بە یەک گۆڕانەوە: دراوەکە Stream<List<Todo>>ـێک دەگەڕێنێتەوە و StreamProviderـێک لە دادەنرێت. هەر جارێک لیستێکی نوێ بنێرێت، providerـەکە دەیدات و شاشەکە دووبارە دروست دەکرێتەوە. سەرچاوە باوەکان: Firestore کە لە هەر گۆڕانێکدا لیستێکی تازە دەنێرێت (بڕوانە خوێندنەوەی زیندوو بە StreamBuilder)، ئاستی باتری، یان دۆخی .

Step 3: a live list with StreamProviderهەنگاوی ٣: لیستێکی زیندوو بە StreamProviderDARTRuns on a phone (or an emulator)لەسەر مۆبایل کار دەکات
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

import 'todo_services.dart';

void main() => runApp(buildApp(TodoDataService().watchTodoList()));

Widget buildApp(Stream<List<Todo>> todoStream) {
  return StreamProvider<List<Todo>>(
    create: (_) => todoStream,
    initialData: const [], // until the first list arrives
    child: const MaterialApp(home: LiveTodoScreen()),
  );
}

class LiveTodoScreen extends StatelessWidget {
  const LiveTodoScreen({super.key});

  @override
  Widget build(BuildContext context) {
    // Rebuilt every time the stream sends a new list.
    final todos = context.watch<List<Todo>>();
    return Scaffold(
      appBar: AppBar(title: Text('Live to-dos (${todos.length})')),
      body: todos.isEmpty
          ? const Center(child: Text('No to-dos yet.'))
          : ListView.builder(
              itemCount: todos.length,
              itemBuilder: (context, i) => ListTile(
                leading: CircleAvatar(child: Text('${todos[i].id}')),
                title: Text(todos[i].title),
              ),
            ),
    );
  }
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 10

The provider subscribes to the stream and cancels the subscription when it leaves the tree.

providerـەکە بەشداری دەکات و کاتێک لە دەردەچێت بەشدارییەکە هەڵدەوەشێنێتەوە.

Lineدێڕی 11

Required here too: the value before the first event.

لێرەشدا پێویستە: نرخەکە پێش یەکەم .

Lineدێڕی 22

The same watch as before: the screen does not know whether a Future or a Stream is behind it.

هەمان watchـی پێشوو: شاشەکە نازانێت یان لە پشتییەوەیە.

Animation in four frames. Left: a pipe labelled watchTodoList(): Stream<List<Todo>> and StreamProvider(initialData: const []); right: a phone titled 'Live to-dos'. Frame 1: the pipe is empty, the phone says 'No to-dos yet.', rebuilds so far: 0. Frame 2: a value [1 item] arrives, the phone shows 'Read chapter 8' highlighted, title Live to-dos (1). Frame 3: [2 items] arrives, 'Call Hana' is added. Frame 4: [3 items], 'Practise layouts' is added; every new value means one rebuild, with no FutureBuilder and no setState.
Animation in four frames. Left: a pipe labelled watchTodoList(): Stream<List<Todo>> and StreamProvider(initialData: const []); right: a phone titled 'Live to-dos'. Frame 1: the pipe is empty, the phone says 'No to-dos yet.', rebuilds so far: 0. Frame 2: a value [1 item] arrives, the phone shows 'Read chapter 8' highlighted, title Live to-dos (1). Frame 3: [2 items] arrives, 'Call Hana' is added. Frame 4: [3 items], 'Practise layouts' is added; every new value means one rebuild, with no FutureBuilder and no setState.
Each new list from the stream: the provider updates, the screen rebuilds.هەر لیستێکی نوێ لە : providerـەکە نوێ دەبێتەوە، شاشەکە دووبارە دروست دەبێتەوە.CêIT
Quick checkپشکنینی خێراA StreamProvider's stream sends three lists, one per second. How many times is a screen that watches it rebuilt after its first build? StreamProviderـێک سێ لیست دەنێرێت، هەر چرکەیەک یەکێک. شاشەیەک کە watchـی دەکات دوای یەکەم buildـی چەند جار دووبارە دروست دەکرێتەوە؟

FutureBuilder, FutureProvider and StreamProvider side by sideFutureBuilder، FutureProvider و StreamProvider لە تەنیشت یەک

Three widget trees in columns. FutureBuilder (no provider yet): a service getTodoList(): Future<List<Todo>> feeds a FutureBuilder that sits under MaterialApp and TodoListScreen; the builder uses snapshot.data. FutureProvider (no more FutureBuilder): the same Future feeds a FutureProvider under MaterialApp; TodoListScreen below it calls context.watch<List<Todo>>(). StreamProvider (live data): watchTodoList(): Stream<List<Todo>> feeds a StreamProvider; TodoListScreen watches it. Each tree ends with Scaffold and ListView. Bottom notes: the screen has a State and a FutureBuilder; the provider waits and the screen only watches; every new list means one more rebuild.سێ داری ویجێتەکان لە سێ ستووندا. FutureBuilder (هێشتا provider نییە): خزمەتگوزارییەک getTodoList(): Future<List<Todo>> دراوە دەدات بە FutureBuilderـێک کە لە ژێر MaterialApp و TodoListScreenـدایە؛ builderـەکە snapshot.data بەکار دەهێنێت. FutureProvider (چیتر FutureBuilder نییە): هەمان نرخی داهاتوو دراوە دەدات بە FutureProviderـێک لە ژێر MaterialAppـدا؛ TodoListScreen لە خوارییەوە بانگی context.watch<List<Todo>>() دەکات. StreamProvider (دراوەی زیندوو): watchTodoList(): Stream<List<Todo>> دراوە دەدات بە StreamProviderـێک؛ TodoListScreen watchـی دەکات. هەر ستوونێک بە Scaffold و ListView کۆتایی دێت. تێبینییەکانی خوارەوە: شاشەکە Stateـی هەیە و FutureBuilderـێک؛ provider چاوەڕێ دەکات و شاشەکە تەنها watch دەکات؛ هەر لیستێکی نوێ واتە دووبارە دروستکردنەوەیەکی تر.
The lecture's three slides, redrawn as one comparison.سێ سلایدەکەی وانەکە، بە شێوەیەکی نوێ وەک یەک بەراورد.CêIT
If the data…ئەگەر دراوەکە…Useبەکاری بهێنە
arrives once and only one screen needs itیەک جار دەگات و تەنها یەک شاشە پێویستی پێیەتیFutureBuilder
arrives once and several screens need itیەک جار دەگات و چەند شاشەیەک پێویستیان پێیەتیFutureProvider
keeps arriving (live) and several screens need itبەردەوام دەگات (زیندوو) و چەند شاشەیەک پێویستیان پێیەتیStreamProvider
keeps arriving and one widget needs itبەردەوام دەگات و یەک پێویستی پێیەتیStreamBuilder
is changed by the app itself (taps, forms)خودی دەیگۆڕێت (دەستلێدان، فۆڕم)ChangeNotifierProvider
Quick checkپشکنینی خێراA news screen and a home-page banner both need today's headlines, fetched once at start-up. What fits best?شاشەی هەواڵەکان و بانەری هەردووکیان پێویستیان بە سەردێڕەکانی ئەمڕۆیە، کە یەک جار لە سەرەتای دەهێنرێن. چی لە هەمووی گونجاوترە؟

Common mistakesهەڵە باوەکان

MistakeهەڵەWhat happensچی ڕوو دەداتFixچاکردنەوە
No initialDatainitialData نییەCompile error in provider 5 and 6 compile لە ⁦provider 5⁩ و 6ـداGive a starting value, or use a nullable type with nullنرخێکی سەرەتایی بدە، یان جۆرێکی nullable لەگەڵ null بەکار بهێنە
Reading List<Todo> when the provider is List<Todo>?خوێندنەوەی List<Todo> لە کاتێکدا providerـەکە List<Todo>?ـەProviderNotFoundExceptionWatch the exact same typeڕێک هەمان جۆر watch بکە
No catchError for a Future or Stream that can failcatchError نییە بۆ یان کە دەتوانێت شکست بهێنێتThe error is reported and the screen keeps initialData ڕادەگەیەنرێت و شاشەکە initialData دەهێڵێتەوەAdd catchError: (_, error) => …catchError: (_, error) => … زیاد بکە
initialData: const [] when “loading” and “empty” must look differentinitialData: const [] کاتێک «بارکردن» و «بەتاڵ» دەبێت جیاواز دەربکەونThe user sees “No to-dos” while the data is still coming «هیچ ئەرکێک نییە» دەبینێت لە کاتێکدا دراوەکە هێشتا دێتUse a nullable type and null for loadingجۆرێکی nullable و null بۆ بارکردن بەکار بهێنە
Using StreamProvider to change stateبەکارهێنانی StreamProvider بۆ گۆڕینی Hard-to-follow code; the provider only shows valuesکۆدێکی قورس بۆ تێگەیشتن؛ providerـەکە تەنها نرخ پیشان دەداتChange state in a ChangeNotifier لە ChangeNotifierـێکدا بگۆڕە
Quick checkپشکنینی خێراA FutureProvider's Future fails because there is no internet, and there is no catchError. What does the screen show? FutureProviderـێک شکست دەهێنێت چونکە نییە و catchError نییە. شاشەکە چی پیشان دەدات؟

Exercisesڕاهێنانەکان

Exerciseڕاهێنان1. From FutureBuilder to FutureProvider١. لە FutureBuilderـەوە بۆ FutureProvider

This screen loads a quote of the day with a FutureBuilder. Rewrite it with a FutureProvider so that the screen becomes a StatelessWidget. Show a spinner while loading and “No quote today.” if loading fails.

ئەم شاشەیە وتەی ڕۆژ بە FutureBuilderـێک بار دەکات. بە FutureProviderـێک دووبارە بینووسەرەوە بۆ ئەوەی شاشەکە ببێت بە . لە کاتی بارکردندا بازنەیەکی چاوەڕوانی و ئەگەر بارکردن شکستی هێنا «⁦No quote today⁩.» پیشان بدە.

DARTRuns on a phone (or an emulator)لەسەر مۆبایل کار دەکات
class QuoteScreenBefore extends StatefulWidget {
  const QuoteScreenBefore({super.key});

  @override
  State<QuoteScreenBefore> createState() => _QuoteScreenBeforeState();
}

class _QuoteScreenBeforeState extends State<QuoteScreenBefore> {
  late final Future<String> _quote = loadQuoteOfTheDay();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: FutureBuilder<String>(
          future: _quote,
          builder: (context, snapshot) {
            if (!snapshot.hasData) return const CircularProgressIndicator();
            return Text(snapshot.data!);
          },
        ),
      ),
    );
  }
}
Hintئاماژە

You need create, initialData, catchError and context.watch<String?>().

پێویستت بە create، initialData، catchError و context.watch<String?>() هەیە.

Show answerوەڵامەکە پیشان بدە
One possible answerوەڵامێکی گونجاوDARTRuns on a phone (or an emulator)لەسەر مۆبایل کار دەکات
void main() {
  runApp(
    FutureProvider<String?>(
      create: (_) => loadQuoteOfTheDay(),
      initialData: null, // still loading
      catchError: (_, _) => 'No quote today.',
      child: const MaterialApp(home: QuoteScreen()),
    ),
  );
}

class QuoteScreen extends StatelessWidget {
  const QuoteScreen({super.key});

  @override
  Widget build(BuildContext context) {
    final quote = context.watch<String?>();
    return Scaffold(
      body: Center(
        child: quote == null
            ? const CircularProgressIndicator()
            : Text(quote),
      ),
    );
  }
}

String? with initialData: null separates loading from a real quote, and catchError turns a failure into a message. In a bigger app, wrap the text in a small class such as Quote: providers are found by type, and many things in an app are Strings.

String? لەگەڵ initialData: null بارکردن لە وتەیەکی ڕاستەقینە جیا دەکاتەوە و catchError شکستێک دەگۆڕێت بۆ پەیامێک. لە گەورەتردا، دەقەکە لە بچووکی وەک Quoteـدا بپێچە: providerـەکان بەپێی جۆر دەدۆزرێنەوە و زۆر شت لە Stringـن.

Exerciseڕاهێنان2. Why does it not compile?٢. بۆچی compile نابێت؟

The slide's code FutureProvider<List<Todo>>(create: (_) => service.getTodoList(), child: const TodoListScreen()) does not compile. Why, and what is the smallest fix?

کۆدەکەی سلایدەکە FutureProvider<List<Todo>>(create: (_) => service.getTodoList(), child: const TodoListScreen()) compile نابێت. بۆچی و بچووکترین چاکردنەوە چییە؟

Show answerوەڵامەکە پیشان بدە

initialData is a required parameter in provider 5 and later, because the screen needs a List<Todo> before the Future completes. The smallest fix is initialData: const []; use FutureProvider<List<Todo>?> with initialData: null if loading must be visible.

initialData لە ⁦provider 5⁩ و دواتردا پێویستە، چونکە شاشەکە پێش تەواوبوونی پێویستی بە List<Todo>ـێکە. بچووکترین چاکردنەوە initialData: const []ـە؛ ئەگەر بارکردن دەبێت دیار بێت، FutureProvider<List<Todo>?> لەگەڵ initialData: null بەکار بهێنە.

Exerciseڕاهێنان3. Choose the provider٣. providerـەکە هەڵبژێرە

For each case, choose FutureBuilder, FutureProvider, StreamProvider or ChangeNotifierProvider: (a) a chat screen that shows new messages as they arrive from Firestore and an unread badge in the app bar; (b) the app's settings, loaded once at start, used on five screens; (c) a cart that the user changes with + and − buttons; (d) one screen that loads a single photo once.

بۆ هەر حاڵەتێک، FutureBuilder، FutureProvider، StreamProvider یان ChangeNotifierProvider هەڵبژێرە: (أ) شاشەیەکی چات کە نامە نوێیەکان لە کاتی گەیشتنیان لە Firestoreـەوە پیشان دەدات و نیشانەیەکی نەخوێندراوە لە ؛ (ب) ڕێکخستنەکانی ، کە یەک جار لە سەرەتادا بار دەکرێن و لە پێنج شاشەدا بەکار دێن؛ (ج) سەبەتەیەک کە بە + و − دەیگۆڕێت؛ (د) یەک شاشە کە یەک وێنە یەک جار بار دەکات.

Show answerوەڵامەکە پیشان بدە
  • (a) StreamProvider: live values, needed in two places.
  • (b) FutureProvider: one value, shared.
  • (c) ChangeNotifierProvider: the app itself changes the data.
  • (d) FutureBuilder: one value, one screen.
  • (أ) StreamProvider: نرخی زیندوو، لە دوو شوێندا پێویستە.
  • (ب) FutureProvider: یەک نرخ، هاوبەش.
  • (ج) ChangeNotifierProvider: خودی دراوەکە دەگۆڕێت.
  • (د) FutureBuilder: یەک نرخ، یەک شاشە.

Summaryپوختە

  • Provider is also a tool for dependency injection: the lecture injects the signed-in User above MaterialApp with Provider<User>.value.
  • FutureProvider creates a Future, waits for it and exposes the result; the screen just watches the value, with no State and no FutureBuilder.
  • StreamProvider listens to a Stream and always exposes the latest value; every new value rebuilds the watchers.
  • initialData is required for both (provider 5+). Use a nullable type with null to show loading, and catchError for failures.
  • Choose by the data: one value for one screen → FutureBuilder; shared → FutureProvider; live → StreamProvider; changed by the app → ChangeNotifierProvider.
  • Provider ئامرازێکیشە بۆ : وانەکە چووە ژوورەوە بە Provider<User>.value لە سەرووی MaterialAppـەوە تێدەخات.
  • FutureProvider دروست دەکات، چاوەڕێی دەکات و ئەنجامەکەی دەدات؛ شاشەکە تەنها نرخەکە watch دەکات، بەبێ State و بەبێ FutureBuilder.
  • StreamProvider گوێ لە دەگرێت و هەمیشە دوایین نرخ دەدات؛ هەر نرخێکی نوێ ئەوانەی watch دەکەن دووبارە دروست دەکاتەوە.
  • initialData بۆ هەردووکیان پێویستە ⁦(provider 5+)⁩. جۆرێکی nullable لەگەڵ null بۆ پیشاندانی بارکردن و catchError بۆ شکستەکان بەکار بهێنە.
  • بەپێی دراوەکە هەڵبژێرە: یەک نرخ بۆ یەک شاشە ← FutureBuilder؛ هاوبەش ← FutureProvider؛ زیندوو ← StreamProvider؛ دەیگۆڕێت ← ChangeNotifierProvider.

Lesson complete!وانەکە تەواو بوو!

Well done. Review the key terms, then go on to the next lesson.دەستخۆش. سەیرێکی زاراوە سەرەکییەکان بکەرەوە، پاشان بڕۆ بۆ وانەی داهاتوو.

Key termsزاراوە سەرەکییەکان

Next lessonوانەی داهاتوو

ProxyProvider and ChangeNotifierProxyProviderProxyProvider و ChangeNotifierProxyProvider

References and further readingسەرچاوەکان و خوێندنەوەی زیاتر

This lecture is a first edition. The Kurdish text and terminology are waiting for review by Kurdish IT teachers.ئەم وانەیە چاپی یەکەمە. دەقە کوردییەکە و زاراوەکانی چاوەڕێی پێداچوونەوەی مامۆستایانی کوردی بواری IT ن.