FlutterFlutter · Chapterبەش 10 · Lectureوانە 10.1

Getting Data from the Internet: HTTP and JSONوەرگرتنی دراوە لە ئینتەرنێت: HTTP و JSON

How a Flutter app talks to a web API: the http package and the Android INTERNET permission, http.get and status codes, jsonDecode, a model class with a fromJson factory (by hand, with pattern matching, or generated by json_serializable), a fetch function that throws on failure, and FutureBuilder screens with waiting, error, data and empty states.

Flutter چۆن قسە لەگەڵ وێب دەکات: http و INTERNETـی Android، ⁦http.get⁩ و ، jsonDecode، بە factoryـی fromJson (بە دەست، بە ناساندنەوەی شێوە یان دروستکراو بە ⁦json_serializable⁩)، هێنانی دراوە کە لە شکستدا فڕێ دەدات و شاشەکانی FutureBuilder بە دۆخەکانی چاوەڕوانی، ، دراوە و بەتاڵ.

21 min read19 خولەک خوێندنەوە · 14 stepsهەنگاو

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

  • Add the http package, declare the INTERNET permission and send a GET request with http.get.
  • Check a response's status code and decode its JSON body with jsonDecode.
  • Write a model class with a fromJson factory, and explain when to generate it with json_serializable.
  • Show network data with FutureBuilder, handling waiting, error, data and empty states, with a retry.
  • http زیاد بکەیت، INTERNET ڕابگەیەنیت و بە ⁦http.get⁩ GET بنێریت.
  • بپشکنیت و جەستە بە jsonDecode بکەیتەوە.
  • بە factoryـی fromJson بنووسیت و ڕوون بکەیتەوە کەی بە ⁦json_serializable⁩ دروستی بکەیت.
  • دراوەی بە FutureBuilder پیشان بدەیت و مامەڵە لەگەڵ دۆخەکانی چاوەڕوانی، ، دراوە و بەتاڵ بکەیت، لەگەڵ دووبارە هەوڵدانەوە.

Apps that ask a serverئەو ئەپانەی پرسیار لە ڕاژەکار دەکەن

Open a weather app, a prayer-times app or a currency app. The numbers you see were not written into the app when it was built. The app asks a server on the internet, the server answers, and the app shows the answer. This happens every time you pull to refresh.

کەشوهەوا، کاتەکانی بانگ یان نرخی دراو بکەرەوە. ئەو ژمارانەی دەیانبینیت لە کاتی دروستکردنی نەنووسراون. پرسیار لە لەسەر دەکات، وەڵام دەداتەوە و وەڵامەکە پیشان دەدات. هەر جارێک شاشەکە بۆ نوێکردنەوە ڕابکێشیت ئەمە ڕوو دەدات.

The question is an HTTP request and the answer is an HTTP response with a status code; you met them in Inside a request and a response. The server's door for apps is an API, and the answer is almost always written in JSON. Those lessons explain HTTP and JSON themselves; this lesson shows only the Flutter side: how a Dart app sends the request, reads the JSON and shows it on the screen.

پرسیارەکە و وەڵامەکە کە لەگەڵدایە؛ لە ناوەوەی داواکاری و وەڵامێک ناسیتن. دەرگای بۆ APIـە و وەڵامەکە نزیکەی هەمیشە بە JSON دەنووسرێت. ئەو وانانە خودی و ڕوون دەکەنەوە؛ ئەم وانەیە تەنها لایەنی Flutter پیشان دەدات: Dart چۆن دەنێرێت، دەخوێنێتەوە و لەسەر شاشە پیشانی دەدات.

A phone running a Flutter app titled Album sends 'GET /albums/1' to a server (jsonplaceholder.typicode.com). The server answers '200 OK' with a JSON body containing userId 1, id 1 and a title. Below, inside the app, four boxes in a row: body: String, then jsonDecode → Map, then Album.fromJson, then Text(album.title).مۆبایلێک کە ئەپێکی Flutter بە ناونیشانی Album تێدا کار دەکات، 'GET /albums/1' بۆ ڕاژەکارێک (jsonplaceholder.typicode.com) دەنێرێت. ڕاژەکارەکە بە '200 OK' و جەستەیەکی JSON وەڵام دەداتەوە کە userId 1، id 1 و ناونیشانێکی تێدایە. لە خوارەوە، لەناو ئەپەکەدا، چوار سندووق لە ڕیزێکدا: body: String، پاشان jsonDecode → Map، پاشان Album.fromJson، پاشان Text(album.title).
The whole journey: request, response, then String → Map → Album → widget.تەواوی گەشتەکە: ، ، پاشان String ← Map ← Album ← .CêIT
Quick checkپشکنینی خێراA Flutter app shows today's exchange rate. Where does the number come from? Flutter نرخی ئەمڕۆی دراو پیشان دەدات. ژمارەکە لە کوێوە دێت؟

The http packageکتێبخانەی http

Dart's core has no short get(url) function for apps. The Flutter team's answer is the http package: a small library that works the same on Android, iOS, the web and desktop. (Dart's own dart:io client does not run on the web, which is why the Flutter docs tell you to use http.) Add it from the terminal in your project folder:

ناوەکی Dart کورتی get(url)ـی بۆ نییە. وەڵامی تیمی Flutter httpـە: بچووک کە لەسەر Android، iOS، وێب و وەک یەک کار دەکات. ( dart:ioـی خودی Dart لەسەر وێب کار ناکات، بۆیە بەڵگەنامەکانی Flutter پێت دەڵێن http بەکار بهێنە.) لە ، لەناو پڕۆژەکەتدا زیادی بکە:

BASH
$ flutter pub add http

The command adds a line under dependencies: in pubspec.yaml and downloads the package (see pubspec.yaml). Then import it with a prefix:

فەرمانەکە دێڕێک لەژێر dependencies: لە pubspec.yamlـدا زیاد دەکات و دەکات (بڕوانە ⁦pubspec.yaml⁩). پاشان لە بە پێشگرێکەوە importـی بکە:

DART
import 'package:http/http.dart' as http;

// later: http.get(...), http.post(...), http.Response
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 1

as http gives the package a short name. You write http.get(...), so a reader always knows the function comes from this package and it cannot clash with another get.

as http ناوێکی کورت دەدات بە . http.get(...) دەنووسیت، بۆیە خوێنەر هەمیشە دەزانێت لەم دێت و لەگەڵ getـێکی تر تێکەڵ نابێت.

Quick checkپشکنینی خێراWhy does the Flutter documentation recommend the http package instead of dart:io's HTTP client?بۆچی بەڵگەنامەکانی Flutter http پێشنیار دەکەن لە جیاتی dart:io؟

The http packageکتێبخانەی http

Permission to use the internetمۆڵەتی ئەپ بۆ بەکارهێنانی ئینتەرنێت

An Android app may use the internet only if it declares it. Add this line to android/app/src/main/AndroidManifest.xml, inside <manifest> and before <application>:

Android تەنها کاتێک دەتوانێت بەکار بهێنێت کە ڕایبگەیەنێت. ئەم دێڕە بۆ android/app/src/main/AndroidManifest.xml زیاد بکە، لەناو <manifest> و پێش <application>:

android/app/src/main/AndroidManifest.xmlفایلی ⁦android/app/src/main/AndroidManifest.xml⁩XML
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- Required to fetch data from the internet. -->
    <uses-permission android:name="android.permission.INTERNET" />
    <application
        ...
    </application>
</manifest>
  • iOS needs no permission for normal HTTPS requests.
  • macOS apps must add com.apple.security.network.client (value true) to macos/Runner/DebugProfile.entitlements and Release.entitlements.
  • Use https://. Plain http:// is unencrypted. Since Android 9 (API 28) and iOS 9, the phone's own network libraries (WebViews, video players, native clients) block it by default. Dart's http client does not check that rule, so an http:// request may work while other parts of the app fail. Always use https://.
  • iOS بۆ ئاسایی هیچ ناوێت.
  • macOS دەبێت com.apple.security.network.client (بە نرخی true) بۆ macos/Runner/DebugProfile.entitlements و Release.entitlements زیاد بکەن.
  • https:// بەکار بهێنە. http://ـی سادە بەبێ . لە ⁦Android 9 (API 28)⁩ و ⁦iOS 9⁩ـەوە، خودی مۆبایلەکە (WebView، لێدەری ڤیدیۆ، ڕەسەن) بە شێوەی بنەڕەتی ڕایدەگرن. httpـی Dart ئەم یاسایە ناپشکنێت، بۆیە لەوانەیە http:// کار بکات و بەشەکانی تری شکست بهێنن. هەمیشە https:// بەکار بهێنە.
Quick checkپشکنینی خێراYour album app loads data on the emulator in debug mode, but users of the published app see only errors. What is the most likely cause? ئەلبومەکەت لەسەر لە دۆخی debugـدا دراوە بار دەکات، بەڵام بڵاوکراوەکە تەنها دەبینن. ئەگەری زۆرتر چییە؟

Sending a GET requestناردنی داواکارییەکی GET

The examples from here on use JSONPlaceholder, a free practice API that returns fake albums, posts and users. It needs no key and no account, so you can try every request yourself. The outputs in this lesson are live data, fetched from JSONPlaceholder in October 2026: the fake data rarely changes, but if it does, your titles will differ.

نموونەکانی ئێرە بەدواوە JSONPlaceholder بەکار دەهێنن، بەخۆڕایی بۆ ڕاهێنان کە ئەلبوم، پۆست و ساختە دەگەڕێنێتەوە. نە و نە هەژماری پێویستە، بۆیە دەتوانیت هەموو خۆت تاقی بکەیتەوە. دەرخستەکانی ئەم وانەیە دراوەی ڕاستەوخۆن کە لە ئۆکتۆبەری ⁦2026⁩دا لە JSONPlaceholderـەوە هێنراون: دراوە ساختەکەی بە دەگمەن دەگۆڕێت، بەڵام ئەگەر بگۆڕێت ناونیشانەکانی تۆ جیاواز دەبن.

http.get takes a Uri (the address) and returns a Future<http.Response>: the request goes out at once, and the response arrives later. So you await it inside an async function, exactly as in async and await. This short program runs with dart run, no phone needed:

http.get (ناونیشانەکە) وەردەگرێت و Future<http.Response> دەگەڕێنێتەوە: دەستبەجێ دەڕوات و دواتر دەگات. بۆیە لەناو asyncـدا awaitـی دەکەیت، ڕێک وەک async و await. ئەم بەرنامە کورتە بە dart run کار دەکات، پێویستی بە مۆبایل نییە:

DARTType in a terminalلە تێرمیناڵدا بینووسە
import 'package:http/http.dart' as http;

Future<void> main() async {
  final url = Uri.parse('https://jsonplaceholder.typicode.com/albums/1');

  final response = await http.get(url);

  print('Status code: ${response.statusCode}');
  print('Content type: ${response.headers['content-type']}');
  print('Body: ${response.body}');
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 4

Uri.parse turns the text address into a Uri object. Uri.https('jsonplaceholder.typicode.com', '/albums/1') builds the same address from parts and encodes it safely. (See three ways to build the same URI.)

Uri.parse ناونیشانە دەقییەکە دەکات بە Uri. Uri.https('jsonplaceholder.typicode.com', '/albums/1') هەمان ناونیشان لە بەشەکانەوە دروست دەکات و بە دروستی ئەنجام دەدات. (بڕوانە سێ ڕێگا بۆ دروستکردنی هەمان URI.)

Lineدێڕی 6

await waits for the response without freezing the app.

await چاوەڕێی دەکات بەبێ ئەوەی ڕابوەستێنێت.

Lineدێڕی 8

statusCode is a number: 200 means OK.

statusCode ژمارەیەکە: ⁦200⁩ واتە OK.

Lineدێڕی 9

headers is a Map of the response's headers.

headers Mapـێکە کە تێدایە.

Lineدێڕی 10

body is the content as one String. It looks like JSON, but Dart sees only text so far.

body ناوەڕۆکەکەیە وەک یەک . وەک دەردەکەوێت، بەڵام Dart تا ئێستا تەنها دەق دەبینێت.

The status codes an app meets most often (full list: Status codes)باوترین لە (لیستی تەواو: کۆدەکانی دۆخ)
Status codeMeaningواتاWhat your app should do چی بکات
200OK: the body has the dataOK: جەستەکە دراوەکەی تێدایەDecode the body and show itجەستەکە بکەرەوە و پیشانی بدە
201Created (answer to a POST)دروستکرا (وەڵامی POST)Show successسەرکەوتن پیشان بدە
404Not found: wrong address or idنەدۆزرایەوە: ناونیشان یان ناسێنەری هەڵەThrow an error; show “not found” فڕێ بدە؛ «نەدۆزرایەوە» پیشان بدە
401 / 403Not signed in (401) / signed in but not allowed (403)نەچوویتە ژوورەوە ⁦(401)⁩ / چوویتە ژوورەوە بەڵام ڕێگەپێدراو نیت ⁦(403)⁩Ask the user to sign in (401) or explain they have no access (403)داوا لە بکە بچێتە ژوورەوە ⁦(401)⁩ یان ڕوونی بکەرەوە کە دەستڕاگەیشتنی نییە ⁦(403)⁩
500The server failed شکستی هێناThrow an error; offer “Try again” فڕێ بدە؛ «دووبارە هەوڵبدەرەوە» پیشنیار بکە
Quick checkپشکنینی خێراWhat is the type of response.body?جۆری response.body چییە؟

REST API with Flutter | Step by step tutorialCodeX · Englishئینگلیزی · 206K viewsبینین · 4.6K likesلایک · Partبەشی 5:03–9:18 (ofلە 11:10)

Send a GET request to JSONPlaceholder with the http package, check the status code and turn the JSON body into model objects.

ناردنی GET بۆ JSONPlaceholder بە پاکێجی http، پشکنینی و گۆڕینی ناوەڕۆکی بۆ Post.

Watch 5:03–9:18, the 'Http call' chapter: http.Client, Uri.parse, get(), a statusCode == 200 check and decoding with the generated postFromJson.

لە ⁦5:03⁩ تا ⁦9:18⁩ سەیر بکە، بەشی Http call: http.Client، Uri.parse، get()، پشکنینی statusCode == 200 و گۆڕینی بە postFromJson، کە خۆکارانە دروست کراوە.

From text to a Map: jsonDecodeلە دەقەوە بۆ Map: jsonDecode

jsonDecode comes from dart:convert (part of Dart, nothing to install). It reads a JSON string and builds Dart values from it. Its return type is dynamic, so you tell Dart what you expect with as:

jsonDecode لە dart:convertـەوە دێت (بەشێکە لە Dart، هیچ شتێک دامەزراندنی ناوێت). دەخوێنێتەوە و نرخی Dartـی لێ دروست دەکات. جۆری dynamicـە، بۆیە بە as بە Dart دەڵێیت چیت چاوەڕوان دەکرد:

DARTType in a terminalلە تێرمیناڵدا بینووسە
import 'dart:convert';

void main() {
  // The body of a response is one long String.
  const body = '{"id": 7, "title": "Kurdish songs", "price": 2.5, '
      '"tags": ["music", "folk"], "isNew": true, "owner": null}';

  final data = jsonDecode(body) as Map<String, dynamic>;

  print(data['title']);
  for (final entry in data.entries) {
    print('${entry.key}: ${entry.value.runtimeType}');
  }

  // The other way: a Dart Map becomes a JSON String.
  final back = jsonEncode({'id': 8, 'title': 'Tea house'});
  print(back);
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 8

A JSON object {...} becomes a Map<String, dynamic>: keys are always strings, values can be anything.

{...} دەبێت بە Map<String, dynamic>: هەمیشە ، نرخەکان دەکرێت هەر شتێک بن.

Lineدێڕی 10

Read a value with its key, like any Map.

نرخێک بە بخوێنەرەوە، وەک هەر Mapـێک.

Lineدێڕی 16

jsonEncode goes the other way: Dart values → JSON text, for example to send in a POST.

jsonEncode بە پێچەوانەوە دەڕوات: نرخەکانی Dart ← دەقی ، بۆ نموونە بۆ ناردن لە POSTـێکدا.

How JSON values arrive in Dartنرخەکانی چۆن دەگەنە Dart
In JSONلە In Dart after jsonDecodeلە Dartـدا دوای jsonDecode
{ "a": 1 }Map<String, dynamic>
[1, 2, 3]List<dynamic>
"text"String
7int
2.5double
true / falsebool
nullnull
Quick checkپشکنینی خێراThe JSON text is [{"id": 1}, {"id": 2}]. What does jsonDecode return?دەقی [{"id": 1}, {"id": 2}]ـە. jsonDecode چی دەگەڕێنێتەوە؟

A model class with fromJsonکلاسێکی مۆدێل بە fromJson

Working with data['title'] everywhere is risky: a typo like data['titel'] compiles and returns null at runtime. The usual fix is a small model class with real, typed fields, and a factory constructor fromJson that turns the Map into an object, exactly as in factory constructors and fromJson. Read the whole JSON once, check it, and from then on the compiler helps you.

کارکردن بە data['title'] لە هەموو شوێنێکدا مەترسیدارە: نووسینی وەک data['titel'] کۆمپایل دەبێت و لە null دەگەڕێنێتەوە. چارەسەری باو بچووکی بە ڕاستەقینە و جۆردار، لەگەڵ factory بە ناوی fromJson کە Mapـەکە دەکات بە ، ڕێک وەک دروستکەری factory و fromJson. هەموو یەک جار بخوێنەرەوە، بیپشکنە و لەوە بەدواوە یارمەتیت دەدات.

DARTType in a terminalلە تێرمیناڵدا بینووسە
import 'dart:convert';

class Album {
  final int userId;
  final int id;
  final String title;

  const Album({required this.userId, required this.id, required this.title});

  // JSON Map -> Album
  factory Album.fromJson(Map<String, dynamic> json) {
    return Album(
      userId: json['userId'] as int,
      id: json['id'] as int,
      title: json['title'] as String,
    );
  }

  // Album -> JSON Map (for sending or saving)
  Map<String, dynamic> toJson() => {'userId': userId, 'id': id, 'title': title};
}

void main() {
  const body = '{"userId": 1, "id": 1, "title": "quidem molestiae enim"}';

  final album = Album.fromJson(jsonDecode(body) as Map<String, dynamic>);
  print('Album ${album.id}: ${album.title}');

  print(jsonEncode(album)); // jsonEncode calls toJson() for you
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 3-8

The model: the same three fields the JSON has, with real types.

: هەمان سێ کە هەیەتی، بە جۆری ڕاستەقینە.

Lineدێڕی 11-17

fromJson reads each key once and checks its type with as. A wrong type throws an error here, at one known place.

fromJson هەر یەک جار دەخوێنێتەوە و جۆرەکەی بە as دەپشکنێت. جۆرێکی هەڵە لێرە فڕێ دەدات، لە یەک شوێنی ناسراودا.

Lineدێڕی 20

toJson is the way back, for saving or sending.

toJson ڕێگای گەڕانەوەیە، بۆ پاشەکەوتکردن یان ناردن.

Lineدێڕی 26

From now on: album.title is a String, with autocomplete, and a typo is a compile error.

لەمەودوا: album.title ، بە تەواوکردنی خۆکار و نووسین .

Lineدێڕی 29

jsonEncode calls toJson() itself.

jsonEncode خۆی بانگی toJson() دەکات.

The Flutter cookbook writes fromJson with Dart 3 pattern matching instead. The switch checks the keys and their types in one step, and throws a FormatException if the data does not fit. Both styles are correct; pattern matching is stricter and shorter when there are many fields:

کتێبی ڕێنماییەکانی ⁦Flutter (cookbook)⁩ fromJson بە ناساندنەوەی شێوە ⁦(pattern matching)⁩ـی ⁦Dart 3⁩ دەنووسێت. switchـەکە و جۆرەکانیان بە یەک هەنگاو دەپشکنێت و ئەگەر دراوەکە نەگونجێت FormatException فڕێ دەدات. هەردوو شێوازەکە دروستن؛ ناساندنەوەی شێوە توندترە و کاتێک زۆرن کورترە:

DARTType in a terminalلە تێرمیناڵدا بینووسە
class Album {
  final int userId;
  final int id;
  final String title;

  const Album({required this.userId, required this.id, required this.title});

  factory Album.fromJson(Map<String, dynamic> json) {
    return switch (json) {
      {'userId': int userId, 'id': int id, 'title': String title} =>
        Album(userId: userId, id: id, title: title),
      _ => throw const FormatException('Failed to load album.'),
    };
  }
}

void main() {
  final good = Album.fromJson({'userId': 1, 'id': 1, 'title': 'quidem'});
  print('OK: ${good.title}');

  try {
    Album.fromJson({'userId': 1, 'id': '1', 'title': 'quidem'}); // id is a String!
  } on FormatException catch (e) {
    print('Rejected: ${e.message}');
  }
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 10-11

The pattern matches only a Map whose userId and id are int and whose title is a String, and binds them to variables.

شێوەکە تەنها لەگەڵ Mapـێک دەگونجێت کە userId و idـی int بن و titleـی String بێت. هەروەها بە دەیانبەستێتەوە.

Lineدێڕی 12

_ matches everything else: wrong types or missing keys.

_ لەگەڵ هەموو شتێکی تر دەگونجێت: جۆری هەڵە یان ونبوو.

Lineدێڕی 22

'1' (a String) is not an int, so the album is rejected instead of breaking later.

'1' () int نییە، بۆیە ئەلبومەکە ڕەت دەکرێتەوە لە جیاتی ئەوەی دواتر تێکبشکێت.

Quick checkپشکنینی خێراWhy write an Album class with fromJson instead of using data['title'] everywhere?بۆچی Album بە fromJson بنووسین لە جیاتی بەکارهێنانی data['title'] لە هەموو شوێنێکدا؟

Putting it together: a fetch functionکۆکردنەوەی پارچەکان: کردارێکی هێنانی دراوە

A good fetch function does four things: send the request, check the status code, decode the body into a model, and throw an exception when anything goes wrong. It never returns null for a failure: an exception is the clear signal that the screen (and FutureBuilder, below) needs. This program runs both a good and a bad request, and also loads a whole list:

باشی هێنانی دراوە چوار کار دەکات: دەنێرێت، دەپشکنێت، جەستەکە دەکاتەوە و دەیکات بە و کاتێک هەر شتێک هەڵە بچێت فڕێ دەدات. هەرگیز بۆ شکست null ناگەڕێنێتەوە: ئەو ئاماژە ڕوونەیە کە شاشەکە (و FutureBuilder، لە خوارەوە) پێویستی پێیەتی. ئەم بەرنامەیە باش و یەکێکی خراپ جێبەجێ دەکات و تەواویش بار دەکات:

DARTType in a terminalلە تێرمیناڵدا بینووسە
import 'dart:convert';

import 'package:http/http.dart' as http;

class Album {
  final int userId;
  final int id;
  final String title;

  const Album({required this.userId, required this.id, required this.title});

  factory Album.fromJson(Map<String, dynamic> json) {
    return Album(
      userId: json['userId'] as int,
      id: json['id'] as int,
      title: json['title'] as String,
    );
  }
}

Future<Album> fetchAlbum(int id) async {
  final response = await http.get(
    Uri.https('jsonplaceholder.typicode.com', '/albums/$id'),
    headers: {'Accept': 'application/json'},
  );

  if (response.statusCode == 200) {
    return Album.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
  } else {
    throw Exception('Failed to load album $id (status ${response.statusCode})');
  }
}

Future<List<Album>> fetchAlbums() async {
  final response =
      await http.get(Uri.https('jsonplaceholder.typicode.com', '/albums'));
  if (response.statusCode != 200) {
    throw Exception('Failed to load albums (status ${response.statusCode})');
  }
  final list = jsonDecode(response.body) as List<dynamic>;
  return list
      .map((item) => Album.fromJson(item as Map<String, dynamic>))
      .toList();
}

Future<void> main() async {
  final album = await fetchAlbum(1);
  print('Got: ${album.title}');

  try {
    await fetchAlbum(9999);
  } catch (e) {
    print('Caught: $e');
  }

  final albums = await fetchAlbums();
  print('${albums.length} albums; the first two:');
  for (final a in albums.take(2)) {
    print('  ${a.id}. ${a.title}');
  }
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 21-25

Uri.https(host, path) builds the address; the Accept header asks politely for JSON.

Uri.https(host, path) ناونیشانەکە دروست دەکات؛ Accept کە بە ڕێزەوە داوای دەکات.

Lineدێڕی 27-28

Only 200 is decoded.

تەنها 200 دەکرێتەوە.

Lineدێڕی 29-31

Any other status code becomes an exception with a useful message.

هەر تر دەبێت بە بە پەیامێکی بەسوود.

Lineدێڕی 40-43

A JSON array becomes a List<dynamic>; map turns every item into an Album.

ڕیزێکی دەبێت بە List<dynamic>؛ map هەموو بڕگەیەک دەکات بە Album.

Lineدێڕی 50-54

The caller decides what to do with the error. Here it is printed; on a screen it becomes an error message.

بانگکەرەکە بڕیار دەدات چی لە بکات. لێرە چاپ دەکرێت؛ لەسەر شاشە دەبێت بە پەیامی .

Quick checkپشکنینی خێراThe server answers 404. What should fetchAlbum do? بە 404 وەڵام دەداتەوە. fetchAlbum چی بکات؟

Putting it together: a fetch functionکۆکردنەوەی پارچەکان: کردارێکی هێنانی دراوە

No internet, slow internetبێ ئینتەرنێت، ئینتەرنێتی خاو

No internet is a different failure from a bad status code. With no connection there is no response at all: http.get itself throws (an http.ClientException, or a SocketException on mobile). Your try/catch, or FutureBuilder's error branch, catches both kinds. To stop waiting forever on a slow network, add a time limit: await http.get(url).timeout(const Duration(seconds: 10)); throws a TimeoutException after 10 seconds.

نەبوونی شکستێکی جیاوازە لە خراپ. بەبێ پەیوەندی هیچ نییە: خودی http.get فڕێ دەدات (http.ClientException، یان لەسەر مۆبایل SocketException). try/catchـەکەت، یان لقی FutureBuilder، هەردوو جۆرەکە دەگرێت. بۆ ئەوەی لەسەر خاو بۆ هەمیشە چاوەڕێ نەکەیت، سنووری کات زیاد بکە: await http.get(url).timeout(const Duration(seconds: 10)); دوای ⁦10⁩ چرکە TimeoutException فڕێ دەدات.

Quick checkپشکنینی خێراThe phone has no connection at all. What happens when the app calls await http.get(url)?مۆبایلەکە هیچ پەیوەندییەکی نییە. کاتێک await http.get(url) بانگ دەکات چی ڕوو دەدات؟

Showing it: FutureBuilder with three statesپیشاندانی: FutureBuilder بە سێ دۆخ

You met FutureBuilder in Preview: FutureBuilder and StreamBuilder. Now it gets real network data. A screen that waits for the internet must always be ready for three situations: still waiting, finished with data, finished with an error. Read the notes line by line:

FutureBuilderـت لە یەکەم سەیر: FutureBuilder و StreamBuilder ناسی. ئێستا دراوەی ڕاستەقینەی وەردەگرێت. شاشەیەک کە چاوەڕێی دەکات دەبێت هەمیشە ئامادەی سێ حاڵەت بێت: هێشتا چاوەڕێ دەکات، بە دراوە تەواو بووە، بە تەواو بووە. تێبینییەکان دێڕ بە دێڕ بخوێنەرەوە:

An album screenشاشەیەکی ئەلبومDARTRuns on a phone (or an emulator)لەسەر مۆبایل کار دەکات
import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

class Album {
  final int userId;
  final int id;
  final String title;

  const Album({required this.userId, required this.id, required this.title});

  factory Album.fromJson(Map<String, dynamic> json) {
    return Album(
      userId: json['userId'] as int,
      id: json['id'] as int,
      title: json['title'] as String,
    );
  }
}

Future<Album> fetchAlbum() async {
  final response = await http.get(
    Uri.https('jsonplaceholder.typicode.com', '/albums/1'),
  );
  if (response.statusCode == 200) {
    return Album.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
  }
  throw Exception('Failed to load album (status ${response.statusCode})');
}

void main() => runApp(const MaterialApp(home: AlbumScreen()));

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

  @override
  State<AlbumScreen> createState() => _AlbumScreenState();
}

class _AlbumScreenState extends State<AlbumScreen> {
  late Future<Album> _album;

  @override
  void initState() {
    super.initState();
    _album = fetchAlbum(); // start the request once
  }

  void _retry() {
    setState(() {
      _album = fetchAlbum(); // a new Future: the builder starts waiting again
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Album')),
      body: Center(
        child: FutureBuilder<Album>(
          future: _album,
          builder: (context, snapshot) {
            // 1. Still waiting for the server
            if (snapshot.connectionState != ConnectionState.done) {
              return const CircularProgressIndicator();
            }
            // 2. Finished with an error
            if (snapshot.hasError) {
              return Column(
                mainAxisSize: MainAxisSize.min,
                children: [
                  Text(
                    'Could not load the album.',
                    style: TextStyle(color: Theme.of(context).colorScheme.error),
                  ),
                  const SizedBox(height: 12),
                  FilledButton(onPressed: _retry, child: const Text('Try again')),
                ],
              );
            }
            // 3. Finished with data
            final album = snapshot.data!;
            return Text(
              album.title,
              style: Theme.of(context).textTheme.headlineSmall,
            );
          },
        ),
      ),
    );
  }
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 42

The Future is kept in the State object.

لە هەڵدەگیرێت.

Lineدێڕی 44-48

initState starts the request once. Calling fetchAlbum() in build would send a new request on every rebuild.

initState یەک جار دەست پێ دەکات. بانگکردنی fetchAlbum() لە buildـدا لە هەر دووبارە دروستکردنەوەیەکدا نوێ دەنێرێت.

Lineدێڕی 50-54

Try again = a new Future inside setState. FutureBuilder sees the new Future and starts waiting again.

دووبارە هەوڵدانەوە = نوێ لەناو setStateـدا. FutureBuilder نوێیەکە دەبینێت و دووبارە دەست بە چاوەڕوانی دەکاتەوە.

Lineدێڕی 62-63

future: is the Future to watch; builder: runs again every time its state changes.

future: ئەو کە چاودێری دەکرێت؛ builder: هەر جارێک دۆخەکەی بگۆڕێت دووبارە جێبەجێ دەبێتەوە.

Lineدێڕی 64-67

State 1: not done yet → a spinner.

دۆخی 1: هێشتا تەواو نەبووە ← بازنەی چاوەڕوانی.

Lineدێڕی 68-81

State 2: done with an error → a red message and a Try again button.

دۆخی 2: بە تەواو بووە ← پەیامێکی سوور و ⁦Try again⁩.

Lineدێڕی 82-87

State 3: done with data → snapshot.data! is the Album.

دۆخی 3: بە دراوە تەواو بووە ← snapshot.data! ئەلبومەکەیە.

Animation in three frames. Left, a phone titled Album; right, the FutureBuilder code with the active lines highlighted. Frame 1: a spinner on the phone, the connectionState check highlighted, label 'Waiting for the server'. Frame 2: the phone shows the album title 'quidem molestiae enim', the last return highlighted, label 'Done with data'. Frame 3: the phone shows the red text 'Could not load the album.' and a Try again button, the hasError branch highlighted, label 'Or done with an error (no internet, 404)'.
Animation in three frames. Left, a phone titled Album; right, the FutureBuilder code with the active lines highlighted. Frame 1: a spinner on the phone, the connectionState check highlighted, label 'Waiting for the server'. Frame 2: the phone shows the album title 'quidem molestiae enim', the last return highlighted, label 'Done with data'. Frame 3: the phone shows the red text 'Could not load the album.' and a Try again button, the hasError branch highlighted, label 'Or done with an error (no internet, 404)'.
The same builder draws three different screens over time.هەمان builder بە درێژایی کات سێ شاشەی جیاواز دەکێشێت.CêIT
Quick checkپشکنینی خێراWhere should fetchAlbum() be called in an album screen?لە شاشەیەکی ئەلبومدا دەبێت لە کوێ بانگی fetchAlbum() بکرێت؟

Showing it: FutureBuilder with three statesپیشاندانی: FutureBuilder بە سێ دۆخ

A list from the serverلیستێک لە ڕاژەکارەوە

Most screens show a list: messages, products, albums. The pattern is the same, with Future<List<Album>> and a ListView.builder that makes rows only for the items on screen. Add a fourth state: the request worked but the list is empty.

زۆربەی شاشەکان پیشان دەدەن: نامەکان، کاڵاکان، ئەلبومەکان. شێوازەکە هەمان شتە، بە Future<List<Album>> و ListView.builder کە تەنها بۆ ئەو بڕگانەی لەسەر شاشەن ڕیز دروست دەکات. دۆخێکی چوارەم زیاد بکە: سەرکەوتوو بوو بەڵام بەتاڵە.

A list of albumsلیستێکی ئەلبومەکانDARTRuns on a phone (or an emulator)لەسەر مۆبایل کار دەکات
import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

class Album {
  final int id;
  final String title;

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

  factory Album.fromJson(Map<String, dynamic> json) =>
      Album(id: json['id'] as int, title: json['title'] as String);
}

Future<List<Album>> fetchAlbums() async {
  final response =
      await http.get(Uri.https('jsonplaceholder.typicode.com', '/albums'));
  if (response.statusCode != 200) {
    throw Exception('Failed to load albums (status ${response.statusCode})');
  }
  final list = jsonDecode(response.body) as List<dynamic>;
  return list
      .map((item) => Album.fromJson(item as Map<String, dynamic>))
      .toList();
}

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

  @override
  State<AlbumListScreen> createState() => _AlbumListScreenState();
}

class _AlbumListScreenState extends State<AlbumListScreen> {
  late final Future<List<Album>> _albums = fetchAlbums();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Albums')),
      body: FutureBuilder<List<Album>>(
        future: _albums,
        builder: (context, snapshot) {
          if (snapshot.connectionState != ConnectionState.done) {
            return const Center(child: CircularProgressIndicator());
          }
          if (snapshot.hasError) {
            return Center(child: Text('Error: ${snapshot.error}'));
          }
          final albums = snapshot.data!;
          if (albums.isEmpty) {
            return const Center(child: Text('No albums yet.'));
          }
          return ListView.builder(
            itemCount: albums.length,
            itemBuilder: (context, index) => ListTile(
              leading: CircleAvatar(child: Text('${albums[index].id}')),
              title: Text(albums[index].title),
            ),
          );
        },
      ),
    );
  }
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 16-26

The list fetch from the previous screen, now with Album.fromJson for each item.

هێنانی لە شاشەی پێشوو، ئێستا بە Album.fromJson بۆ هەر بڕگەیەک.

Lineدێڕی 36

late final with an initializer: the request starts the first time _albums is read (during the first build) and never again.

late final بە نرخی سەرەتاییەوە: یەکەم جار کە _albums دەخوێنرێتەوە (لە یەکەم buildـدا) دەست پێ دەکات و هەرگیز دووبارە نابێتەوە.

Lineدێڕی 52-54

Empty is not an error: tell the user plainly.

بەتاڵ نییە: بە ڕوونی بە بڵێ.

Lineدێڕی 55-61

ListView.builder builds one ListTile per album, only when it scrolls into view.

ListView.builder بۆ هەر ئەلبومێک یەک ListTile دروست دەکات، تەنها کاتێک دێتە ناو شاشەوە.

Quick checkپشکنینی خێراThe server returns [] with status 200. Which branch should the list screen show? [] بە ⁦200⁩ دەگەڕێنێتەوە. شاشەی کام لق پیشان بدات؟

Writing fromJson by hand or generating itنووسینی fromJson بە دەست یان دروستکردنی خۆکاری

Some languages read JSON into objects automatically by inspecting classes while the program runs (reflection). Flutter turns this off: if the tools cannot see which code is used, they cannot remove unused code from your app, and the app gets bigger. So in Flutter you either write fromJson yourself or let a tool generate it before the app is built.

هەندێک زمان خۆکارانە دەکەن بە بە پشکنینی لە کاتی کارکردنی بەرنامەدا (reflection). Flutter ئەمە ناچالاک دەکات: ئەگەر ئامرازەکان نەتوانن ببینن کام کۆد بەکار دێت، ناتوانن کۆدی بەکارنەهاتوو لە لاببەن و گەورەتر دەبێت. بۆیە لە Flutterـدا یان fromJson خۆت دەنووسیت یان ڕێگە دەدەیت ئامرازێک پێش دروستی بکات.

QuestionپرسیارBy hand (dart:convert)بە دەست ⁦(dart:convert)⁩Generated (json_serializable)خۆکاری ⁦(json_serializable)⁩
SetupئامادەکردنNoneهیچThree packages and a build commandسێ و فەرمانێکی build
Typing workکاری نووسینGrows with every fieldلەگەڵ هەر زیاد دەکاتOne annotation per classیەک نیشانە بۆ هەر
A misspelled key بە هەڵە نووسراوFound at runtimeلە دەدۆزرێتەوەImpossible: the tool writes the keysمەحاڵە: ئامرازەکە دەنووسێت
Best forباشترین بۆSmall apps, a few models, learning بچووک، چەند ، فێربوونMedium and large apps with many models مامناوەند و گەورە بە زۆر

Code generation in three steps (shown for the same Album):

دروستکردنی خۆکاری کۆد بە سێ هەنگاو (بۆ هەمان Album):

BASH
$ flutter pub add json_annotation dev:build_runner dev:json_serializable
lib/album.dartDART
import 'package:json_annotation/json_annotation.dart';

part 'album.g.dart';

@JsonSerializable()
class Album {
  final int userId;
  final int id;
  final String title;

  const Album({required this.userId, required this.id, required this.title});

  factory Album.fromJson(Map<String, dynamic> json) => _$AlbumFromJson(json);

  Map<String, dynamic> toJson() => _$AlbumToJson(this);
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 3

part links the file the generator will write. Until you run it, the editor shows an error here; that is expected.

part ئەو دەبەستێتەوە کە دروستکەرەکە دەینووسێت. تا جێبەجێی نەکەیت، دەستکاریکەرەکە لێرە پیشان دەدات؛ ئەمە چاوەڕوانکراوە.

Lineدێڕی 5

@JsonSerializable() asks the tool to write the code for this class.

@JsonSerializable() داوا لە ئامرازەکە دەکات کۆدی ئەم بنووسێت.

Lineدێڕی 13, 15

Your class only forwards to the generated functions _$AlbumFromJson and _$AlbumToJson.

تەنها دەینێرێت بۆ ئەو ئامرازەکە دروستی کردوون: _$AlbumFromJson و _$AlbumToJson.

BASH
$ dart run build_runner build --delete-conflicting-outputs

Look at what the tool wrote: (json['userId'] as num).toInt(). It accepts 1 and 1.0 alike, which avoids the classic int/double mistake you will see under Common mistakes. Use build_runner watch instead of build to regenerate every time you save.

سەیری ئەوە بکە کە ئامرازەکە نووسیویەتی: (json['userId'] as num).toInt(). 1 و 1.0 وەک یەک وەردەگرێت، کە ڕێگری لە هەڵە بەناوبانگەکەی ⁦int/double⁩ دەکات کە لە بەشی هەڵە باوەکاندا دەیبینیت. لە جیاتی build، build_runner watch بەکار بهێنە بۆ ئەوەی هەر جارێک پاشەکەوتی دەکەیت سەرلەنوێ دروستی بکاتەوە.

Quick checkپشکنینی خێراWhy has Flutter no built-in way to turn any JSON into any object automatically?بۆچی Flutter ڕێگایەکی ناوەکیی نییە بۆ ئەوەی هەر خۆکارانە بکات بە هەر ؟

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

One mistake catches almost everyone: a number in JSON has no separate “int” and “double” type. If the server sends 2000 for a price, Dart decodes an int, and as double throws:

هەڵەیەک نزیکەی هەمووان دەگرێت: ژمارە لە جۆری جیای «int» و «double»ـی نییە. ئەگەر بۆ نرخێک 2000 بنێرێت، Dart intـێک دەکاتەوە و as double فڕێ دەدات:

DARTType in a terminalلە تێرمیناڵدا بینووسە
void main() {
  final json = {'name': 'Notebook', 'price': 2000}; // the server sent 2000, not 2000.0

  try {
    final price = json['price'] as double; // wrong: the value is an int
    print(price);
  } catch (e) {
    print('Error: $e');
  }

  final price = (json['price'] as num).toDouble(); // right: accept int or double
  print('Price: $price');
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 5

Wrong: 2000 is an int, not a double.

هەڵە: 2000 intـە، نەک double.

Lineدێڕی 11

Right: read it as num (int or double), then convert.

دروست: وەک num (int یان double) بیخوێنەرەوە، پاشان بیگۆڕە.

MistakeهەڵەWhat happensچی ڕوو دەداتFixچاکردنەوە
No INTERNET permission in the main AndroidManifest.xml INTERNET لە ⁦AndroidManifest.xml⁩ـی سەرەکیدا نییەDebug works; the release app gets only errorsdebug کار دەکات؛ release تەنها وەردەگرێتAdd <uses-permission android:name="android.permission.INTERNET" /><uses-permission android:name="android.permission.INTERNET" /> زیاد بکە
Calling the fetch function inside buildبانگکردنی هێنانی دراوە لەناو buildA new request on every rebuild; flicker; wasted data نوێ لە هەر دووبارە دروستکردنەوەیەکدا؛ لەرزین؛ بەفیڕۆدانی دراوەStart it in initState and keep the Future in a fieldلە initStateـدا دەستی پێ بکە و لە هەڵبگرە
Not checking statusCodeنەپشکنینی statusCodeA 404 or 500 page is decoded as if it were dataپەڕەیەکی ⁦404⁩ یان ⁦500⁩ وەک دراوە دەکرێتەوەDecode only on 200; throw otherwiseتەنها لە ⁦200⁩ـدا بیکەرەوە؛ ئەگەرنا فڕێ بدە
Returning null on failureگەڕاندنەوەی null لە کاتی شکستداThe screen spins or shows nothing; the real error is lostشاشەکە دەسووڕێتەوە یان هیچ پیشان نادات؛ ڕاستەقینەکە ون دەبێتThrow an exception; let hasError show it فڕێ بدە؛ با hasError پیشانی بدات
Forgetting awaitلەبیرکردنی awaitresponse is a Future, so .body does not existresponse Futureـێکە، بۆیە .bodyـی نییەfinal response = await http.get(url);final response = await http.get(url);
as double for a JSON numberas double بۆ ژمارەیەکی Crash when the server sends 2000تێکشکان کاتێک 2000 دەنێرێت(json['price'] as num).toDouble()(json['price'] as num).toDouble()
Using http://بەکارهێنانی http://Data travels unencrypted; WebViews and native clients block it on Android 9+ and iOSدراوە بەبێ دەگوازرێتەوە؛ WebView و ڕەسەن لە ⁦Android 9+⁩ و iOSـدا ڕایدەگرنUse https://https:// بەکار بهێنە
Building URLs by joining stringsدروستکردنی URL بە لکاندنی Spaces and Kurdish letters break the addressبۆشایی و پیتی کوردی ناونیشانەکە تێک دەدەنUri.https(host, path, {'q': text}) encodes for youUri.https(host, path, {'q': text}) خۆی دەکات

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

Exerciseڕاهێنان1. A nested object١. ئۆبجێکتێکی ناوەکی

Decode {"name": "Shilan", "address": {"city": "Erbil", "street": "100m"}} and print Shilan lives in Erbil. (The address is a JSON object inside a JSON object.)

{"name": "Shilan", "address": {"city": "Erbil", "street": "100m"}} بکەرەوە و Shilan lives in Erbil. چاپ بکە. (ناونیشانەکە لەناو .)

Hintئاماژە

user['address'] is itself a Map.

user['address'] خۆی Mapـێکە.

Show answerوەڵامەکە پیشان بدە
DARTType in a terminalلە تێرمیناڵدا بینووسە
import 'dart:convert';

void main() {
  const body = '{"name": "Shilan", "address": {"city": "Erbil", "street": "100m"}}';

  final user = jsonDecode(body) as Map<String, dynamic>;
  final address = user['address'] as Map<String, dynamic>; // a nested object

  print('${user['name']} lives in ${address['city']}.');
}

A nested JSON object is a Map inside the Map, so read it with as Map<String, dynamic> before using its keys.

ناوەکی Mapـێکە لەناو Mapـدا، بۆیە پێش بەکارهێنانی ، بە as Map<String, dynamic> بیخوێنەرەوە.

Exerciseڕاهێنان2. A Post model٢. مۆدێلی Post

https://jsonplaceholder.typicode.com/posts/1 returns userId, id, title and body. Write a Post class with a pattern-matching fromJson, a fetchPost(int id) function that checks the status code, and print the post's title.

https://jsonplaceholder.typicode.com/posts/1 userId، id، title و body دەگەڕێنێتەوە. Post بنووسە بە fromJsonـێکی ناساندنەوەی شێوە، fetchPost(int id) کە دەپشکنێت. پاشان ناونیشانی پۆستەکە چاپ بکە.

Show answerوەڵامەکە پیشان بدە
DARTType in a terminalلە تێرمیناڵدا بینووسە
import 'dart:convert';

import 'package:http/http.dart' as http;

class Post {
  final int userId;
  final int id;
  final String title;
  final String body;

  const Post({
    required this.userId,
    required this.id,
    required this.title,
    required this.body,
  });

  factory Post.fromJson(Map<String, dynamic> json) {
    return switch (json) {
      {
        'userId': int userId,
        'id': int id,
        'title': String title,
        'body': String body,
      } =>
        Post(userId: userId, id: id, title: title, body: body),
      _ => throw const FormatException('Not a post.'),
    };
  }
}

Future<Post> fetchPost(int id) async {
  final response =
      await http.get(Uri.https('jsonplaceholder.typicode.com', '/posts/$id'));
  if (response.statusCode != 200) {
    throw Exception('Post $id: status ${response.statusCode}');
  }
  return Post.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
}

Future<void> main() async {
  final post = await fetchPost(1);
  print('Post ${post.id} by user ${post.userId}: ${post.title}');
}
Exerciseڕاهێنان3. Count the finished to-dos٣. ئەرکە تەواوبووەکان بژمێرە

Request /todos with the query parameter userId=1 (use the third argument of Uri.https). Print the full URL, the number of to-dos and how many have completed equal to true.

داوای /todos بکە بە userId=1 (ئارگیومێنتی سێیەمی Uri.https بەکار بهێنە). URLـە تەواوەکە، ژمارەی ئەرکەکان و چەندیان completedـیان trueـە چاپ بکە.

Show answerوەڵامەکە پیشان بدە
DARTType in a terminalلە تێرمیناڵدا بینووسە
import 'dart:convert';

import 'package:http/http.dart' as http;

Future<void> main() async {
  final url = Uri.https('jsonplaceholder.typicode.com', '/todos', {'userId': '1'});
  final response = await http.get(url);
  if (response.statusCode != 200) {
    throw Exception('Status ${response.statusCode}');
  }

  final todos = jsonDecode(response.body) as List<dynamic>;
  final done = todos.where((t) => (t as Map<String, dynamic>)['completed'] == true);

  print(url);
  print('User 1 has ${todos.length} to-dos; ${done.length} are completed.');
}

Uri.https added ?userId=1 for you. The body is a JSON array, so it decodes to a List, and where keeps the completed ones.

Uri.https خۆی ?userId=1ـی زیاد کرد. جەستەکە ڕیزێکی ، بۆیە دەبێت بە List و where تەواوبووەکان دەهێڵێتەوە.

Exerciseڕاهێنان4. A message for every status code٤. پەیامێک بۆ هەر کۆدێکی دۆخ

Write String messageFor(int statusCode) that returns a short message a user can understand for 200, 401/403, 404, any 5xx code, and anything else. Test it with 200, 403, 404, 503 and 418.

String messageFor(int statusCode) بنووسە کە پەیامێکی کورت و تێگەیشتوو بۆ دەگەڕێنێتەوە بۆ ⁦200⁩، ⁦401/403⁩، ⁦404⁩، هەر کۆدێکی ⁦5xx⁩ و هەر شتێکی تر. بە ⁦200⁩، ⁦403⁩، ⁦404⁩، ⁦503⁩ و ⁦418⁩ تاقیی بکەرەوە.

Show answerوەڵامەکە پیشان بدە
DARTType in a terminalلە تێرمیناڵدا بینووسە
String messageFor(int statusCode) => switch (statusCode) {
      200 => 'OK',
      401 || 403 => 'Please sign in, or ask for access.',
      404 => 'We could not find that item.',
      >= 500 && < 600 => 'The server has a problem. Try again later.',
      _ => 'Unexpected answer ($statusCode).',
    };

void main() {
  for (final code in [200, 403, 404, 503, 418]) {
    print('$code -> ${messageFor(code)}');
  }
}

A Dart 3 switch expression can test several values (401 || 403) and ranges (>= 500 && < 600).

دەربڕینی switchـی ⁦Dart 3⁩ دەتوانێت چەند نرخێک (401 || 403) و هەموو نرخەکانی نێوان دوو سنوور (>= 500 && < 600) تاقی بکاتەوە.

Exerciseڕاهێنان5. A user screen with Try again٥. شاشەی بەکارهێنەر بە ⁦Try again⁩

Build UserScreen(userId: 3) that loads /users/3 and shows the name and e-mail in a ListTile. While waiting show a spinner; on error show a button that loads again.

UserScreen(userId: 3) دروست بکە کە /users/3 بار دەکات و ناو و ئیمەیڵ لە ListTileـێکدا پیشان دەدات. لە کاتی چاوەڕوانیدا بازنەی چاوەڕوانی پیشان بدە؛ لە کاتی پیشان بدە کە دووبارە بار دەکاتەوە.

Show answerوەڵامەکە پیشان بدە
DARTRuns on a phone (or an emulator)لەسەر مۆبایل کار دەکات
import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

class User {
  final String name;
  final String email;

  const User({required this.name, required this.email});

  factory User.fromJson(Map<String, dynamic> json) =>
      User(name: json['name'] as String, email: json['email'] as String);
}

Future<User> fetchUser(int id) async {
  final response =
      await http.get(Uri.https('jsonplaceholder.typicode.com', '/users/$id'));
  if (response.statusCode != 200) {
    throw Exception('User $id: status ${response.statusCode}');
  }
  return User.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
}

class UserScreen extends StatefulWidget {
  const UserScreen({super.key, required this.userId});

  final int userId;

  @override
  State<UserScreen> createState() => _UserScreenState();
}

class _UserScreenState extends State<UserScreen> {
  late Future<User> _user;

  @override
  void initState() {
    super.initState();
    _user = fetchUser(widget.userId);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('User')),
      body: Center(
        child: FutureBuilder<User>(
          future: _user,
          builder: (context, snapshot) {
            if (snapshot.connectionState != ConnectionState.done) {
              return const CircularProgressIndicator();
            }
            if (snapshot.hasError) {
              return TextButton(
                onPressed: () =>
                    setState(() => _user = fetchUser(widget.userId)),
                child: const Text('Could not load. Tap to try again.'),
              );
            }
            final user = snapshot.data!;
            return ListTile(
              leading: const Icon(Icons.person),
              title: Text(user.name),
              subtitle: Text(user.email),
            );
          },
        ),
      ),
    );
  }
}

The retry button assigns a new Future inside setState; widget.userId reads the value the parent passed in.

دووبارە هەوڵدانەوە نوێ لەناو setStateـدا دادەنێت؛ widget.userId ئەو نرخە دەخوێنێتەوە کە ناردوویەتی.

Quick checkپشکنینی خێراWhich order is right inside a fetch function?کام ڕیزبەندی لەناو هێنانی دراوەدا دروستە؟

Summaryپوختە

  • Add the http package (flutter pub add http) and import it as http. Declare the INTERNET permission in the main AndroidManifest.xml, and use https.
  • await http.get(uri) gives an http.Response: statusCode, headers and body (a String).
  • jsonDecode turns the body into Map<String, dynamic> or List<dynamic>; jsonEncode goes back.
  • A model class with a fromJson factory checks the JSON once and gives typed fields; write it by hand or generate it with json_serializable.
  • A fetch function checks the status code and throws on failure; it never returns null.
  • Start the Future in initState; draw it with FutureBuilder: waiting → spinner, error → message + Try again, data → the content (and an empty-list message).
  • http زیاد بکە (flutter pub add http) و بە پێشگری as http importـی بکە. INTERNET لە ⁦AndroidManifest.xml⁩ـی سەرەکیدا ڕابگەیەنە و https بەکار بهێنە.
  • await http.get(uri) http.Responseـێک دەدات: statusCode، headers و body ().
  • jsonDecode جەستەکە دەکات بە Map<String, dynamic> یان List<dynamic>؛ jsonEncode بە پێچەوانەوە.
  • بە factoryـی fromJson یەک جار دەپشکنێت و جۆردار دەدات؛ بە دەست بینووسە یان بە ⁦json_serializable⁩ دروستی بکە.
  • هێنانی دراوە دەپشکنێت و لە کاتی شکستدا فڕێ دەدات؛ هەرگیز null ناگەڕێنێتەوە.
  • لە initStateـدا دەست پێ بکە؛ بە FutureBuilder بیکێشە: چاوەڕوانی ← بازنەی چاوەڕوانی، ← پەیام + ⁦Try again⁩، دراوە ← ناوەڕۆکەکە (و پەیامی بەتاڵ).
Review questionsپرسیارەکانی پێداچوونەوە
Why does a release app need the INTERNET line when the debug app works without it?بۆچی ئەپی release پێویستی بە دێڕی INTERNET هەیە کاتێک ئەپی debug بەبێ ئەو کار دەکات؟

The debug manifest already declares INTERNET for the Flutter tool. The release build reads only the main manifest.

src/debug/AndroidManifest.xml پێشتر INTERNET بۆ ئامرازی Flutter ڕادەگەیەنێت. release تەنها src/main/AndroidManifest.xml دەخوێنێتەوە.

What does jsonDecode return for a JSON object, and why use fromJson after it?jsonDecode بۆ ئۆبجێکتێکی JSON چی دەگەڕێنێتەوە و بۆچی دوای ئەو fromJson بەکار دەهێنین؟

A Map<String, dynamic>. fromJson checks the keys and types once and gives a typed object, so mistakes are found early.

Map<String, dynamic>. fromJson و جۆرەکان یەک جار دەپشکنێت و جۆردار دەدات، بۆیە هەڵەکان زوو دەدۆزرێنەوە.

Name the three states a FutureBuilder screen must handle.سێ دۆخەکە ناو ببە کە شاشەیەکی FutureBuilder دەبێت مامەڵەیان لەگەڵ بکات.

Waiting (not done), done with an error, done with data.

چاوەڕوانی (تەواو نەبووە)، تەواو بە ، تەواو بە دراوە.

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

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

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

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

Saving Data on the Deviceپاشەکەوتکردنی دراوە لەسەر ئامێرەکە

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

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