FlutterFlutter · Chapterبەش 9 · Lectureوانە 9.4

Building URIs in Dart: SMS and E-mail Linksدروستکردنی URI لە Dartـدا: بەستەرەکانی نامەی کورت و ئیمەیڵ

Dart's Uri class (scheme, host, path, query, queryParameters, fragment; Uri.parse and Uri.https); the host-or-path bug; percent-encoding Kurdish text with Uri.encodeComponent, with real output; building sms: and mailto: links without double encoding (query: versus queryParameters and the + sign, the url_launcher README helper); and why query values must be strings.

Uriـی (scheme، host، path، query، queryParameters، fragment؛ ⁦Uri.parse⁩ و ⁦Uri.https⁩)؛ هەڵەی یان ؛ گۆڕینی دەقی کوردی بە و ⁦Uri.encodeComponent⁩، بە ڕاستەقینەوە؛ دروستکردنی sms: و mailto: بەبێ دوو جار گۆڕین (query: بەراورد بە queryParameters و نیشانەی +، یاریدەدەرەکەی READMEـی ⁦url_launcher⁩)؛ و بۆچی نرخەکانی دەبێت بن.

18 min read16 خولەک خوێندنەوە · 17 stepsهەنگاو

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

  • Build and read URIs with Dart's Uri class: Uri.parse, the Uri constructor and Uri.https.
  • Spot and fix the host-or-path bug.
  • Encode text, including Kurdish, with Uri.encodeComponent and read the result.
  • Build sms: and mailto: links that are encoded exactly once, with string values.
  • بە Uriـی دروست بکەیت و بیخوێنیتەوە: ⁦Uri.parse⁩، Uri و ⁦Uri.https⁩.
  • هەڵەی یان بدۆزیتەوە و چاکی بکەیت.
  • دەق، لەوانە دەقی کوردی، بە ⁦Uri.encodeComponent⁩ بگۆڕیت و ئەنجامەکە بخوێنیتەوە.
  • sms: و mailto: دروست بکەیت کە ڕێک یەک جار گۆڕدراون، بە نرخی .

From the idea to Dart codeلە بیرۆکەوە بۆ کۆدی Dart

The previous lesson opened links with launchUrl. Each link was a Uri object. A website link is easy to write as a string, but SMS and e-mail links carry a number, a message, a subject. They must be built correctly: the right part in the right place, and every space, symbol and Kurdish letter encoded once.

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

What a URI is, its parts and the idea of percent-encoding are taught in the shared lesson URL, URI and URN. This lesson is the Dart side: the Uri class and the course's SMS and e-mail examples, run and corrected.

چییە، بەشەکانی و بیرۆکەی لە وانە هاوبەشەکەدا دەخوێنرێن: URL، URI و URN. ئەم وانەیە لایەنی : Uri و نموونەکانی و وانەکە، جێبەجێکراو و ڕاستکراوە.

Quick checkپشکنینی خێراWhich import do you need before you can use Uri?پێش بەکارهێنانی Uri پێویستت بە کام import هەیە؟

The Uri class: a form with named boxesکلاسی Uri: فۆرمێک بە خانەی ناودارەوە

The paper describes the Uri constructor well: it has several parameters you give values to, such as scheme and path. Each parameter is a named parameter for one part of the URI. Think of a parcel form at the post office: one box for the city, one for the street, one for a note. Dart then writes the address in the correct order, with the right : // ? # signs between the parts.

نووسینەکە Uri باش وەسف دەکات: چەند هەیە کە نرخیان پێ دەدەیت، وەک scheme و path. هەر بۆ یەک بەشی . بیر لە فۆرمی پاکەت لە پۆستەخانە بکەرەوە: خانەیەک بۆ شار، یەکێک بۆ شەقام، یەکێک بۆ تێبینی. پاشان ناونیشانەکە بە ڕیزی دروست دەنووسێت، لەگەڵ نیشانەکانی : // ? # لە نێوان بەشەکاندا.

A form titled 'Uri( … )' with labelled boxes: scheme 'https', host 'www.example.com', port '8080', path '/courses/flutter', queryParameters {'lang': 'ckb'}, fragment 'intro'. An arrow goes down to the finished URI https://www.example.com:8080/courses/flutter?lang=ckb#intro, where each part is underlined in the colour of its box and the separators :// : ? # are shown in grey as 'added by Dart'.فۆرمێک بە ناونیشانی «Uri( … )» بە خانەی ناونراوەوە: scheme «https»، host «www.example.com»، port «8080»، path «/courses/flutter»، queryParameters {'lang': 'ckb'}، fragment «intro». تیرێک دەچێتە خوارەوە بۆ URI تەواوبووەکە https://www.example.com:8080/courses/flutter?lang=ckb#intro، کە هەر بەشێکی بە ڕەنگی خانەکەی هێڵی بەژێردا هاتووە و جیاکەرەوەکانی :// : ? # بە خۆڵەمێشی وەک «Dart زیادیان دەکات» پیشان دراون.
Fill in the boxes; Dart puts the parts in order and adds the separators.خانەکان پڕ بکەرەوە؛ بەشەکان ڕیز دەکات و جیاکەرەوەکان زیاد دەکات.CêIT
Quick checkپشکنینی خێراYou fill in only the values. Which signs does Dart add by itself between the parts?تۆ تەنها نرخەکان پڕ دەکەیتەوە. خۆی کام نیشانانە لە نێوان بەشەکاندا زیاد دەکات؟

The Uri class: a form with named boxesکلاسی Uri: فۆرمێک بە خانەی ناودارەوە

The named parameters, one by oneپارامیتەرە ناودارەکان، یەک بە یەک

The named parameters of the Uri constructor Uri
ParameterPart of the URIبەشی Example valueنموونەی نرخ
schemeThe scheme: which kind of link: کام جۆرە 'https', 'tel', 'sms', 'mailto'
hostThe host: which server (only for links with //): کام (تەنها بۆ ئەو //ـیان تێدایە)'www.example.com'
portThe port number, if not the defaultژمارەی ، ئەگەر بنەڕەتی نەبێت8080
pathThe path: which resource; a phone number or an e-mail address in tel:, sms: and mailto:: کام سەرچاوە؛ ژمارە تەلەفۆن یان لە tel:، sms: و mailto:ـدا'/courses/flutter', '07500000000'
pathSegmentsThe path as a list of pieces; Dart encodes each piece وەک لیستێک لە پارچە؛ هەر پارچەیەک دەگۆڕێت['courses', 'flutter']
queryThe query, as a string you have already encoded، وەک کە پێشتر خۆت گۆڕیوتە'body=Hello%20Aram'
queryParametersThe query, as a Map that Dart encodes for you، وەک Mapـێک کە بۆت دەیگۆڕێت{'lang': 'ckb'}
fragmentThe fragment: a place inside the page: شوێنێک لەناو لاپەڕەکەدا'intro'
userInfoA user name before the host (rare)ناوی پێش (کەم)'aram'
Quick checkپشکنینی خێراWhich parameter takes a Map and encodes it for you?کام Mapـێک وەردەگرێت و بۆت دەیگۆڕێت؟

The Uri class: a form with named boxesکلاسی Uri: فۆرمێک بە خانەی ناودارەوە

Scheme and path in the paperسکیم و ڕێڕەو لە نووسینەکەدا

The paper explains two of them. The word scheme means a format, plan or system: it names the kind of URI you are building. If the scheme is https, the URI is a web address (a URL). The path is then the address of the resource you want to reach. Be careful with one detail: the website's name (www.example.com) is not part of the path, but the host. The next screen shows what happens when you mix them up.

نووسینەکە دووانیان ڕوون دەکاتەوە. وشەی واتە شێوە، پلان یان سیستەم: جۆری ئەو ناو دەبات کە دروستی دەکەیت. ئەگەر https بێت، ناونیشانێکی وێبە (). ئەوکات ناونیشانی ئەو سەرچاوەیەیە کە دەتەوێت پێی بگەیت. ئاگاداری وردەکارییەک بە: ناوی (www.example.com) بەشێک نییە لە ، بەڵکو . شاشەی داهاتوو پیشان دەدات چی ڕوو دەدات کاتێک تێکەڵیان دەکەیت.

Quick checkپشکنینی خێراIn Uri(scheme: 'tel', path: '07500000000'), what is 07500000000?لە Uri(scheme: 'tel', path: '07500000000')ـدا، 07500000000 چییە؟

The Uri class: a form with named boxesکلاسی Uri: فۆرمێک بە خانەی ناودارەوە

Reading the parts of a URIخوێندنەوەی بەشەکانی URIـیەک

Uri.parse turns a string into a Uri object, and each part is then a property you can read. queryParameters even gives the query as a Map, already decoded:

Uri.parse دەکاتە Uri و پاشان هەر بەشێک کە دەتوانیت بیخوێنیتەوە. queryParameters تەنانەت وەک Mapـێکی گەڕێنراوە دەدات:

Uri.parse and its parts⁦Uri.parse⁩ و بەشەکانیDARTType in a terminalلە تێرمیناڵدا بینووسە
void main() {
  final uri = Uri.parse(
      'https://www.example.com:8080/courses/flutter?lang=ckb&page=2#intro');

  print('scheme:   ${uri.scheme}');
  print('host:     ${uri.host}');
  print('port:     ${uri.port}');
  print('path:     ${uri.path}');
  print('query:    ${uri.query}');
  print('params:   ${uri.queryParameters}');
  print('fragment: ${uri.fragment}');
  print('lang is   ${uri.queryParameters['lang']}');
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 2-3

One string with every part: scheme, host, port, path, query, fragment.

یەک بە هەموو بەشەکانەوە: ، ، ، ، ، .

Lineدێڕی 10

The query as a Map: {lang: ckb, page: 2}.

وەک Map: {lang: ckb, page: 2}.

Lineدێڕی 12

Read one value by its key.

یەک نرخ بە بخوێنەرەوە.

Quick checkپشکنینی خێراWhat does Uri.parse('https://www.example.com/a?x=1#top').fragment give?Uri.parse('https://www.example.com/a?x=1#top').fragment چی دەدات؟

The Uri class: a form with named boxesکلاسی Uri: فۆرمێک بە خانەی ناودارەوە

Three ways to build the same URIسێ ڕێگا بۆ دروستکردنی هەمان URI

You can write the whole string and parse it, fill in the named parts, or use the Uri.https shortcut, which takes the host, the path and a Map of query parameters. All three give the same object:

دەتوانیت هەموو بنووسیت و parseـی بکەیت، بەشە ناودارەکان پڕ بکەیتەوە، یان کورتکراوەی Uri.https بەکار بهێنیت کە ، و Mapـێکی وەردەگرێت. هەر سێکیان هەمان دەدەن:

Uri.parse, Uri() and Uri.https⁦Uri.parse⁩، ⁦Uri()⁩ و ⁦Uri.https⁩DARTType in a terminalلە تێرمیناڵدا بینووسە
void main() {
  // 1. From a string
  final a = Uri.parse('https://www.example.com/courses/flutter?lang=ckb');

  // 2. From named parts
  final b = Uri(
    scheme: 'https',
    host: 'www.example.com',
    path: '/courses/flutter',
    queryParameters: {'lang': 'ckb'},
  );

  // 3. With the Uri.https shortcut: host, path, query
  final c = Uri.https('www.example.com', '/courses/flutter', {'lang': 'ckb'});

  print(a);
  print(b);
  print(c);
  print(a == b && b == c);
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 3

Good when you already have the full address as text.

باشە کاتێک پێشتر ناونیشانە تەواوەکەت وەک دەق هەیە.

Lineدێڕی 6-11

Good when the parts come from variables, such as a search word.

باشە کاتێک بەشەکان لە دێن، وەک وشەیەک کە بەدوایدا دەگەڕێت.

Lineدێڕی 14

Uri.https(host, path, queryParameters); there is also Uri.http.

Uri.https(host, path, queryParameters)؛ Uri.httpـیش هەیە.

Quick checkپشکنینی خێراWhich call builds https://www.example.com/search?q=dart?کام بانگکردن https://www.example.com/search?q=dart دروست دەکات؟

The host-or-path bugهەڵەی هۆست یان ڕێڕەو

This is the paper's first example. It wants to open Google's website:

ئەمە یەکەم نموونەی نووسینەکەیە. دەیەوێت Google بکاتەوە:

The paper's codeکۆدەکەی نووسینەکەDART
MyMethod() {
  Uri uri = Uri(scheme: 'https', path: 'www.google.com');
  launchUrl(uri);
}

Run only the Uri part and print it, and the problem appears:

تەنها بەشی Uri جێبەجێ بکە و چاپی بکە، کێشەکە دەردەکەوێت:

Host or path?هۆست یان ڕێڕەو؟DARTType in a terminalلە تێرمیناڵدا بینووسە
void main() {
  // The notes' version: the name was put in `path`
  final wrong = Uri(scheme: 'https', path: 'www.google.com');
  print('wrong: $wrong');
  print('  host: "${wrong.host}"  path: "${wrong.path}"');

  // Correct: the name goes in `host`
  final right = Uri(scheme: 'https', host: 'www.google.com');
  print('right: $right');
  print('  host: "${right.host}"  path: "${right.path}"');

  // Two shorter ways that also put the name in the host
  print(Uri.https('www.google.com'));
  print(Uri.parse('https://www.google.com'));
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 3

The paper's version: the website's name is put in path.

وەشانەکەی نووسینەکە: ناوی خراوەتە path.

Lineدێڕی 8

Correct: the name is the host.

دروست: ناوەکە hostـە.

Lineدێڕی 13-14

Both shortcuts put the name in the host for you.

هەردوو کورتکراوەکە ناوەکە بۆت دەخەنە .

https:www.google.com has no // and no host, so a browser cannot open it. Dart wrote exactly what it was given: a scheme and a path. The same trap is explained without code in host or path?.

https:www.google.com نە //ـی هەیە و نە ، بۆیە ناتوانێت بیکاتەوە. ڕێک ئەوەی نووسی کە پێی درابوو: و . هەمان تەڵە بەبێ کۆد لە هۆست یان ڕێڕەو؟ ڕوون کراوەتەوە.

Quick checkپشکنینی خێراWhat does Uri(scheme: 'https', path: 'www.example.com').toString() give?Uri(scheme: 'https', path: 'www.example.com').toString() چی دەدات؟

Encoding text with Uri.encodeComponentگۆڕینی دەق بە ⁦Uri.encodeComponent⁩

A message such as "سڵاو! موحەممەد!" cannot go into a URI as it is. Some special characters must be prepared first: the space, &, ?, #, , and every Kurdish letter. Uri.encodeComponent does this: it turns one piece of text, such as a message, into percent-encoded form, so it can be sent safely as one part of a URI.

نامەیەکی وەک «سڵاو! موحەممەد!» وەک خۆی ناتوانێت بچێتە ناو . هەندێک تایبەت دەبێت سەرەتا ئامادە بکرێن: بۆشایی، &، ?، #، , و هەموو پیتێکی کوردی. Uri.encodeComponent ئەمە دەکات: پارچەیەک دەق، وەک نامەیەک، دەگۆڕێت بۆ شێوەی ، بۆ ئەوەی بە سەلامەتی وەک یەک بەشی بنێردرێت.

Encoding a Kurdish messageگۆڕینی نامەیەکی کوردیDARTType in a terminalلە تێرمیناڵدا بینووسە
void main() {
  const message = 'سڵاو! موحەممەد!';

  final encoded = Uri.encodeComponent(message);
  print(encoded);

  // Decoding gives the original text back
  print(Uri.decodeComponent(encoded));
  print(Uri.decodeComponent(encoded) == message);

  // Letter by letter
  for (final letter in ['س', 'ڵ', 'ا', 'و', '!', ' ']) {
    print("'$letter' -> ${Uri.encodeComponent(letter)}");
  }
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 4-5

Encode the whole message once.

هەموو نامەکە یەک جار بگۆڕە.

Lineدێڕی 8-9

Uri.decodeComponent turns it back; nothing is lost.

Uri.decodeComponent دەیگەڕێنێتەوە؛ هیچ شتێک ون نابێت.

Lineدێڕی 12-14

The first letters one by one.

یەکەم پیتەکان یەک بە یەک.

Animation in four frames. The message «سڵاو! موحەممەد!» is shown as boxes, one per character. 1: the first letter س turns into %D8%B3 (two bytes). 2: ڵ, ا and و become %DA%B5, %D8%A7 and %D9%88. 3: the exclamation mark stays ! and the space becomes %20. 4: the whole result %D8%B3%DA%B5%D8%A7%D9%88!%20%D9%85…%D8%AF! with a note: each Kurdish letter becomes two %XX codes, the space becomes %20, and encodeComponent leaves ! as it is.
Animation in four frames. The message «سڵاو! موحەممەد!» is shown as boxes, one per character. 1: the first letter س turns into %D8%B3 (two bytes). 2: ڵ, ا and و become %DA%B5, %D8%A7 and %D9%88. 3: the exclamation mark stays ! and the space becomes %20. 4: the whole result %D8%B3%DA%B5%D8%A7%D9%88!%20%D9%85…%D8%AF! with a note: each Kurdish letter becomes two %XX codes, the space becomes %20, and encodeComponent leaves ! as it is.
Uri.encodeComponent, letter by letter: Kurdish letters become two %XX codes each, the space %20.⁦Uri.encodeComponent⁩، پیت بە پیت: هەر پیتێکی کوردی دەبێتە دوو کۆدی %XX و بۆشایی %⁦20⁩.CêIT

Read the output: every Kurdish letter is two bytes in UTF-8, so it becomes two %XX codes (س → %D8%B3). The space becomes %20. The ! stays as it is: encodeComponent leaves only the English letters A–Z and a–z, the digits 0–9 and - _ . ! ~ * ' ( ) unchanged, and encodes everything else.

بخوێنەرەوە: هەر پیتێکی کوردی لە ⁦UTF-8⁩ـدا دوو ، بۆیە دەبێتە دوو کۆدی %XX (س ← %D8%B3). بۆشاییەکە دەبێتە %20. ! وەک خۆی دەمێنێتەوە: encodeComponent تەنها پیتە ئینگلیزییەکانی A–Z و a–z، ژمارەکانی 0–9 و - _ . ! ~ * ' ( ) وەک خۆیان دەهێڵێتەوە و هەموو شتێکی تر دەگۆڕێت.

Quick checkپشکنینی خێراWhat does Uri.encodeComponent('چا شیرین') do with the space?Uri.encodeComponent('چا شیرین') چی بە بۆشاییەکە دەکات؟

Encoding text with Uri.encodeComponentگۆڕینی دەق بە ⁦Uri.encodeComponent⁩

encodeComponent, encodeQueryComponent and encodeFullencodeComponent، encodeQueryComponent و encodeFull

Dart has more than one encoding function, and they differ in small but important ways. Real outputs:

زیاتر لە یەک گۆڕینی هەیە و بە شێوەی بچووک بەڵام گرنگ جیاوازن. ڕاستەقینە:

Dart's encoding functions (Dart 3.12) گۆڕینی ⁦(Dart 3.12)⁩
FunctionInputOutput (dart run) ⁦(dart run)⁩Use it forبۆ چی بەکاری دەهێنیت
Uri.encodeComponentHello, how are you?Hello%2C%20how%20are%20you%3FOne part: a message, a subject, a path pieceیەک بەش: نامەیەک، بابەتێک، پارچەیەکی
Uri.encodeQueryComponentHello, how are you?Hello%2C+how+are+you%3FHTML-form style queries (space → +) شێوەی فۆرمی HTML (بۆشایی ← +)
Uri.encodeFullhttps://www.example.com/a b?q=x yhttps://www.example.com/a%20b?q=x%20yA whole URI: it keeps / ? & = # تەواو: / ? ⁦& = #⁩ دەهێڵێتەوە
Uri.decodeComponentHello%2C%20howHello, howTurning codes back into textگەڕاندنەوەی کۆدەکان بۆ دەق

encodeFull cannot be used for a message: it keeps & and =, so a message with & would still break the query. For one piece of text, use encodeComponent.

encodeFull بۆ نامە بەکار نایەت: & و = دەهێڵێتەوە، بۆیە نامەیەک کە &ـی تێدا بێت هێشتا تێک دەدات. بۆ یەک پارچە دەق، encodeComponent بەکار بهێنە.

Quick checkپشکنینی خێراWhich function turns the space in a message into %20 and also encodes &?کام بۆشایی لە نامەیەکدا دەکاتە %20 و &ـیش دەگۆڕێت؟

Building an sms: linkدروستکردنی بەستەرێکی sms:

To open Messages with a number and a ready text, the URI needs a query. The paper introduces the parameter that works with key and value pairs: queryParameters. For SMS the key is body, so the end of the URI looks like ?body=value. Here is the notes' code, with the paper's version of the same idea:

بۆ کردنەوەی نامەکان بە ژمارەیەک و دەقێکی ئامادەوە، پێویستی بە هەیە. نووسینەکە ئەو دەناسێنێت کە بە کار دەکات: queryParameters. بۆ bodyـە، بۆیە کۆتایی وەک ?body=value دەردەکەوێت. ئەمە کۆدەکەی تێبینییەکانە، لەگەڵ وەشانەکەی نووسینەکە بۆ هەمان بیرۆکە:

The notes' SMS codeکۆدی نامەی کورتی تێبینییەکانDART
smsMethod() async{

  final Uri smsURL = Uri(
    scheme: 'sms',
    path: '0770893',
    queryParameters: <String, String>{
      'body': Uri.encodeComponent("My Message"),
    },
  );

  await launchUrl(smsURL);
}
The paper's first SMS exampleیەکەم نموونەی نامەی کورتی نووسینەکەDART
uri = Uri(
  scheme: 'sms',
  path: '07500000000',
  queryParameters: <String, String>{
    'body': Uri.encodeComponent(smsmessage ?? ''),
  },
);

Look at the outputs: My%2520Message and %25D8%25B3…. %25 is the code of the % sign itself. The text was encoded twice: once by encodeComponent, then again by queryParameters, which always encodes its values. The messaging app decodes once and shows My%20Message instead of My Message.

سەیری بکە: My%2520Message و %25D8%25B3…. %25 کۆدی خودی نیشانەی %ـە. دەقەکە دوو جار گۆڕدراوە: جارێک بە encodeComponent، پاشان دووبارە بە queryParameters کە هەمیشە نرخەکانی دەگۆڕێت. نامەکان یەک جار دەیگەڕێنێتەوە و لە جیاتی My Message، My%20Message پیشان دەدات.

Quick checkپشکنینی خێراA link arrives in Messages as Hello%20Aram. What is the most likely cause? لە نامەکاندا وەک Hello%20Aram دەگات. هۆکاری ئەگەری زۆری چییە؟

Building an sms: linkدروستکردنی بەستەرێکی sms:

Encode once: three ways comparedیەک جار بیگۆڕە: بەراوردی سێ ڕێگا

Three ways, comparedسێ ڕێگا، بەراوردکراوDARTType in a terminalلە تێرمیناڵدا بینووسە
void main() {
  const number = '1234567890';
  const message = 'Hello, how are you?';

  // A. The notes' way: encodeComponent INSIDE queryParameters
  final a = Uri(
    scheme: 'sms',
    path: number,
    queryParameters: {'body': Uri.encodeComponent(message)},
  );

  // B. queryParameters with the plain text
  final b = Uri(
    scheme: 'sms',
    path: number,
    queryParameters: {'body': message},
  );

  // C. query with encodeComponent (url_launcher's advice)
  final c = Uri(
    scheme: 'sms',
    path: number,
    query: 'body=${Uri.encodeComponent(message)}',
  );

  print('A: $a');
  print('B: $b');
  print('C: $c');
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 9

A: encoded by hand, then encoded again by queryParameters.

A: بە دەست گۆڕدراوە، پاشان دووبارە queryParameters دەیگۆڕێت.

Lineدێڕی 16

B: queryParameters alone: encoded once, but spaces become +.

B: تەنها queryParameters: یەک جار دەگۆڕدرێت، بەڵام بۆشاییەکان دەبنە +.

Lineدێڕی 23

C: query: takes the string as it is; encodeComponent encoded it once, with %20.

C: query: وەک خۆی وەردەگرێت؛ encodeComponent یەک جار گۆڕیویەتی، بە %20.

Before and after: what the user sees in Messagesپێش و دوای: لە نامەکاندا چی دەبینێت
WayڕێگاThe message arrives asنامەکە وەک ئەمە دەگاتVerdictبڕیار
A: queryParameters + encodeComponentڕێگای A: queryParameters + encodeComponentHello%2C%20how%20are%20you%3FWrong: encoded twiceهەڵە: دوو جار گۆڕدراوە
B: queryParameters onlyڕێگای B: تەنها queryParametersHello, how are you? or Hello,+how+are+you?Risky: some SMS apps show the + signsمەترسیدار: هەندێک نیشانەکانی + پیشان دەدەن
C: query: + encodeComponentڕێگای C: query: + encodeComponentHello, how are you?Right: the README's advice for sms: and mailto:دروست: ئامۆژگاریی README بۆ sms: و mailto:

Way C gives exactly the result the paper shows for its second example, the message "Hello, how are you?" to 1234567890: sms:1234567890?body=Hello%2C%20how%20are%20you%3F. Why not B? queryParameters uses HTML-form encoding, where a space is +. Web servers understand that, but sms: and mailto: links are read by apps that may treat + as a real plus sign. The mailto standard (RFC 6068) says spaces SHOULD be written as %20, and the url_launcher README says: for schemes other than http and https, build the query with query: and Uri.encodeComponent.

ڕێگای C ڕێک ئەو ئەنجامە دەدات کە نووسینەکە بۆ نموونەی دووەمی پیشانی دەدات، نامەی «⁦Hello, how are you?⁩» بۆ 1234567890: sms:1234567890?body=Hello%2C%20how%20are%20you%3F. بۆچی B نا؟ queryParameters گۆڕینی شێوەی فۆرمی HTML بەکار دەهێنێت کە تێیدا بۆشایی +ـە. لەمە تێدەگەن، بەڵام sms: و mailto: لەلایەن ئەو دەخوێنرێنەوە کە لەوانەیە + وەک نیشانەی کۆی ڕاستەقینە وەربگرن. ستانداردی ⁦mailto (RFC 6068)⁩ دەڵێت بۆشایی دەبێت وەک %20 بنووسرێت. READMEـی ⁦url_launcher⁩ـیش دەڵێت: بۆ جگە لە http و https، بە query: و Uri.encodeComponent دروست بکە.

A reusable sendSms functionکردارێکی sendSms کە دووبارە بەکار دێتەوەDARTRuns on a phone (or an emulator)لەسەر مۆبایل کار دەکات
import 'package:url_launcher/url_launcher.dart';

Future<void> sendSms(String number, String message) async {
  final Uri smsUrl = Uri(
    scheme: 'sms',
    path: number,
    query: 'body=${Uri.encodeComponent(message)}',
  );

  if (!await launchUrl(smsUrl)) {
    throw Exception('Could not open Messages for $smsUrl');
  }
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 7

Encode the message once, then give the finished string to query:.

نامەکە یەک جار بگۆڕە، پاشان تەواوبووەکە بدە بە query:.

Lineدێڕی 10-12

Open Messages; report a failure.

نامەکان بکەرەوە؛ شکست ڕابگەیەنە.

Quick checkپشکنینی خێراWhich line builds sms:1234567890?body=See%20you?کام دێڕ sms:1234567890?body=See%20you دروست دەکات؟

Building an sms: linkدروستکردنی بەستەرێکی sms:

Values in queryParameters must be stringsنرخەکانی queryParameters دەبێت زنجیرەنووسە بن

The paper says queryParameters takes a <String, dynamic> Map: the keys are strings and the values may be any type. That is not quite right. Dart's documentation says each value must be null, a String, or an Iterable of strings (for a key that appears several times). Run it:

نووسینەکە دەڵێت queryParameters Mapـێکی <String, dynamic> وەردەگرێت: و نرخەکان دەتوانن هەر جۆرێک بن. ئەمە تەواو ڕاست نییە. بەڵگەنامەکانی دەڵێن هەر نرخێک دەبێت null، String یان Iterableـێک لە بێت (بۆ کە چەند جار دەردەکەوێت). جێبەجێی بکە:

String, list of strings, intString، لیستی زنجیرەنووسەکان، intDARTType in a terminalلە تێرمیناڵدا بینووسە
void main() {
  // A list of strings gives the same key twice
  print(Uri(
    scheme: 'https',
    host: 'www.example.com',
    path: '/search',
    queryParameters: {
      'tag': ['dart', 'flutter'],
    },
  ));

  // A number is not allowed
  try {
    print(Uri(scheme: 'sms', path: '1234567890', queryParameters: {'n': 5}));
  } catch (e) {
    print('Error: $e');
  }

  // Convert it to a String first
  const count = 5;
  print(Uri(
      scheme: 'sms',
      path: '1234567890',
      queryParameters: {'n': count.toString()}));
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 8

A list gives the key twice: tag=dart&tag=flutter.

لیستێک دوو جار دەدات: tag=dart&tag=flutter.

Lineدێڕی 14

An int compiles (the parameter type is dynamic) but fails when the program runs.

intـێک کۆمپایل دەبێت (جۆری dynamicـە) بەڵام لە شکست دەهێنێت.

Lineدێڕی 23

Convert numbers with toString() first.

سەرەتا ژمارەکان بە toString() بگۆڕە.

Quick checkپشکنینی خێراYou want ?page=3 from final page = 3;. What do you write?دەتەوێت لە final page = 3;ـەوە ?page=3 دروست بکەیت. چی دەنووسیت؟

Building a mailto: linkدروستکردنی بەستەرێکی ئیمەیڵ

An e-mail link has the address as its path, and the subject and body in the query. The notes write the query by hand:

ناونیشانەکەی وەک و بابەت و دەقەکەی لە هەیە. تێبینییەکان بە دەست دەنووسن:

The notes' e-mail codeکۆدی ئیمەیڵی تێبینییەکانDART
sendEmail() async{

  final Uri emailURL = Uri(
    scheme: 'mailto',
    path: 'abc@example.com',
    query: 'subject=Important Question&body=Dear Students, this ...',
  );

  await launchUrl(emailURL);
}

This one works: with query:, Dart encodes the spaces as %20 and keeps & and = as separators. But that is also the danger. If the subject itself contains & or =, Dart cannot know it is text:

ئەمەیان کار دەکات: بە query:، بۆشاییەکان وەک %20 دەگۆڕێت و & و = وەک جیاکەرەوە دەهێڵێتەوە. بەڵام مەترسییەکەش ئەوەیە. ئەگەر خودی بابەتەکە & یان =ـی تێدا بێت، ناتوانێت بزانێت ئەمە دەقە:

A hand-typed query breaks; the README helper does notزنجیرەیەکی پرسی بە دەست نووسراو تێک دەچێت؛ یاریدەدەرەکەی README ناDARTType in a terminalلە تێرمیناڵدا بینووسە
String? encodeQueryParameters(Map<String, String> params) {
  return params.entries
      .map((e) =>
          '${Uri.encodeComponent(e.key)}=${Uri.encodeComponent(e.value)}')
      .join('&');
}

void main() {
  // The notes' way: the query typed by hand
  final byHand = Uri(
    scheme: 'mailto',
    path: 'teacher@example.com',
    query: 'subject=Important Question&body=Dear Students, this ...',
  );
  print(byHand);

  // A subject that contains & breaks the hand-typed query
  final broken = Uri(
    scheme: 'mailto',
    path: 'teacher@example.com',
    query: 'subject=Q&A session&body=See you',
  );
  print(broken);
  print(broken.queryParameters);

  // With the helper from the url_launcher README
  final safe = Uri(
    scheme: 'mailto',
    path: 'teacher@example.com',
    query: encodeQueryParameters({
      'subject': 'Q&A session',
      'body': 'See you at 10:00, room 3.',
    }),
  );
  print(safe);
  print(safe.queryParameters);
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 1-7

The helper from the url_launcher README: encode each key and value with encodeComponent, then join them with = and &.

یاریدەدەرەکەی READMEـی ⁦url_launcher⁩: هەر و نرخێک بە encodeComponent بگۆڕە، پاشان بە = و & پێکەوەیان ببەستە.

Lineدێڕی 21

The subject Q&A session contains &…

بابەتی Q&A session &ـی تێدایە…

Lineدێڕی 24

…so the app would read the subject as just Q.

…بۆیە بابەتەکە تەنها وەک Q دەخوێنێتەوە.

Lineدێڕی 30-33

With the helper, & becomes %26 and the subject arrives whole.

بە یاریدەدەرەکە، & دەبێتە %26 و بابەتەکە تەواو دەگات.

sendEmail with the README helpersendEmail بە یاریدەدەرەکەی READMEDARTRuns on a phone (or an emulator)لەسەر مۆبایل کار دەکات
import 'package:url_launcher/url_launcher.dart';

String? encodeQueryParameters(Map<String, String> params) {
  return params.entries
      .map((MapEntry<String, String> e) =>
          '${Uri.encodeComponent(e.key)}=${Uri.encodeComponent(e.value)}')
      .join('&');
}

Future<void> sendEmail() async {
  final Uri emailUrl = Uri(
    scheme: 'mailto',
    path: 'teacher@example.com',
    query: encodeQueryParameters(<String, String>{
      'subject': 'Important Question',
      'body': 'Dear teacher, can we meet at 10:00?',
    }),
  );

  if (!await launchUrl(emailUrl)) {
    throw Exception('Could not open the e-mail app for $emailUrl');
  }
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 3-8

The helper returns the finished query string.

یاریدەدەرەکە ئامادە دەگەڕێنێتەوە.

Lineدێڕی 13

The address is the path. There is no host in mailto: links.

ناونیشانەکە . لە هیچ نییە.

Lineدێڕی 14-17

Subject and body as a Map, encoded once.

بابەت و دەق وەک Map، یەک جار گۆڕدراو.

Quick checkپشکنینی خێراWhy does the hand-typed query 'subject=Q&A session&body=See you' go wrong?بۆچی بە دەست نووسراوی 'subject=Q&A session&body=See you' هەڵە دەڕوات؟

Putting it together: a contact screenکۆکردنەوەی هەمووی: شاشەیەکی پەیوەندی

A small screen that uses everything from this chapter: a tel: link, an sms: link with a Kurdish message, a mailto: link with the helper, and a website, all through one _open method that shows a SnackBar when nothing can open the link:

شاشەیەکی بچووک کە هەموو شتێکی ئەم بەشە بەکار دەهێنێت: tel:، sms: بە نامەیەکی کوردییەوە، بە یاریدەدەرەکەوە و ، هەموویان لە ڕێگەی یەک _openـەوە کە کاتێک هیچ شتێک ناتوانێت بکاتەوە پیشان دەدات:

Contact usپەیوەندیمان پێوە بکەDARTRuns on a phone (or an emulator)لەسەر مۆبایل کار دەکات
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';

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

String? encodeQueryParameters(Map<String, String> params) {
  return params.entries
      .map((e) =>
          '${Uri.encodeComponent(e.key)}=${Uri.encodeComponent(e.value)}')
      .join('&');
}

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

  static const String _phone = '07500000000';
  static const String _email = 'office@example.com';

  Future<void> _open(BuildContext context, Uri uri) async {
    bool opened;
    try {
      opened = await launchUrl(uri);
    } catch (e) {
      opened = false;
    }
    if (opened || !context.mounted) return;
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text('No app on this phone can open $uri')),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Contact us')),
      body: ListView(
        children: [
          ListTile(
            leading: const Icon(Icons.call),
            title: const Text('Call'),
            subtitle: const Text(_phone),
            onTap: () => _open(context, Uri(scheme: 'tel', path: _phone)),
          ),
          ListTile(
            leading: const Icon(Icons.sms),
            title: const Text('Send an SMS'),
            onTap: () => _open(
              context,
              Uri(
                scheme: 'sms',
                path: _phone,
                query: 'body=${Uri.encodeComponent('سڵاو! پرسیارێکم هەیە.')}',
              ),
            ),
          ),
          ListTile(
            leading: const Icon(Icons.email),
            title: const Text('Send an e-mail'),
            subtitle: const Text(_email),
            onTap: () => _open(
              context,
              Uri(
                scheme: 'mailto',
                path: _email,
                query: encodeQueryParameters({
                  'subject': 'Question about the course',
                  'body': 'Hello, I have a question.',
                }),
              ),
            ),
          ),
          ListTile(
            leading: const Icon(Icons.language),
            title: const Text('Website'),
            onTap: () => _open(context, Uri.https('www.example.com')),
          ),
        ],
      ),
    );
  }
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 19-30

One safe way to open any URI (see when nothing can open the link).

یەک ڕێگای سەلامەت بۆ کردنەوەی هەر (بڕوانە کاتێک هیچ شتێک ناتوانێت بەستەرەکە بکاتەوە).

Lineدێڕی 42

tel: with the number as the path.

tel: بە ژمارەکە وەک .

Lineدێڕی 49-53

sms: with the Kurdish text encoded once, through query:.

sms: بە دەقە کوردییەکەوە کە یەک جار گۆڕدراوە، لە ڕێگەی query:ـەوە.

Lineدێڕی 62-69

mailto: with the README helper.

mailto: بە یاریدەدەرەکەی README.

Lineدێڕی 75

Uri.https puts the name in the host.

Uri.https ناوەکە دەخاتە .

Quick checkپشکنینی خێراIn the contact screen, why does the SMS link use query: and not queryParameters:?لە شاشەی پەیوەندیدا، بۆچی query: بەکار دەهێنێت نەک queryParameters:؟

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

MistakeهەڵەWhat you getچی وەردەگریتFixچاککردن
Uri(scheme: 'https', path: 'www.example.com')https:www.example.comhost: 'www.example.com', Uri.https('www.example.com') or Uri.parse('https://www.example.com')host: 'www.example.com'، Uri.https('www.example.com') یان Uri.parse('https://www.example.com')
encodeComponent inside queryParametersencodeComponent لەناو queryParametersـداbody=My%2520Messagequery: 'body=${Uri.encodeComponent(msg)}'query: 'body=${Uri.encodeComponent(msg)}'
queryParameters for sms:/mailto:queryParameters بۆ ⁦sms:/mailto⁩:body=See+youquery: with encodeComponent or the README helperquery: بە encodeComponent یان یاریدەدەرەکەی README
A hand-typed query with & or = in the text بە دەست نووسراو کە & یان =ـی لە دەقەکەدا هەیە{subject: Q, A session: , …}Encode each value (encodeQueryParameters)هەر نرخێک بگۆڕە (encodeQueryParameters)
An int in queryParametersintـێک لە queryParametersـداTypeError at run timevalue.toString()value.toString()
Uri.encodeFull for a messageUri.encodeFull بۆ نامەیەک& and = stay and break the query& و = دەمێننەوە و تێک دەدەنUri.encodeComponentUri.encodeComponent
Uri.encodeComponent(component: text), as in the paper's pictureUri.encodeComponent(component: text)، وەک وێنەکەی نووسینەکەCompile error: there is no named parameter component کۆمپایل: هیچ بە ناوی component نییەUri.encodeComponent(text): component was only the editor's hintUri.encodeComponent(text): component تەنها ئاماژەی دەستکاریکەرەکە بوو

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

Exerciseڕاهێنان1. The paper's second SMS example١. دووەم نموونەی نامەی کورتی نووسینەکە

Build in Dart the URI that opens Messages for the number 1234567890 with the text Hello, how are you?. Which string do you expect?

لە ئەو دروست بکە کە نامەکان بۆ ژمارەی 1234567890 بە دەقی Hello, how are you? دەکاتەوە. چاوەڕێی کام دەکەیت؟

Hintئاماژە

Use query: and encode the message once.

query: بەکار بهێنە و نامەکە یەک جار بگۆڕە.

Show answerوەڵامەکە پیشان بدە
DART
final uri = Uri(
  scheme: 'sms',
  path: '1234567890',
  query: 'body=${Uri.encodeComponent('Hello, how are you?')}',
);
print(uri);
Exerciseڕاهێنان2. Find the bug٢. هەڵەکە بدۆزەرەوە

A classmate's code prints https:www.example.com/books. Write the line that probably made it, and two correct versions.

کۆدی هاوپۆلێک https:www.example.com/books چاپ دەکات. ئەو دێڕە بنووسە کە ئەگەری زۆرە دروستی کردبێت و دوو وەشانی دروست.

Show answerوەڵامەکە پیشان بدە
DART
// probably:
Uri(scheme: 'https', path: 'www.example.com/books');

// correct:
Uri(scheme: 'https', host: 'www.example.com', path: '/books');
Uri.https('www.example.com', '/books');
Exerciseڕاهێنان3. Decode by hand٣. بە دەست بیگەڕێنەرەوە

Without running code, decode sms:07500000000?body=%DA%A9%D9%88%D8%B1%D8%AF using this table: %DA%A9 = ک, %D9%88 = و, %D8%B1 = ر, %D8%AF = د. Then check it with Uri.decodeComponent.

بەبێ جێبەجێکردنی کۆد، sms:07500000000?body=%DA%A9%D9%88%D8%B1%D8%AF بەم خشتەیە بگەڕێنەرەوە: %DA%A9 = ک، %D9%88 = و، %D8%B1 = ر، %D8%AF = د. پاشان بە Uri.decodeComponent بیپشکنە.

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

Each pair of codes is one letter: ک، و، ر، د. The body is «کورد».

هەر جووتێک کۆد یەک پیتە: ک، و، ر، د. دەقەکە «کورد»ـە.

DART
print(Uri.decodeComponent('%DA%A9%D9%88%D8%B1%D8%AF'));
Exerciseڕاهێنان4. An e-mail with symbols٤. ئیمەیڵێک بە هێماوە

Write a mailto: URI for office@example.com with the subject Marks & attendance and the body Is room 3 = room C?. Use the README helper and say what & and = become.

mailto: بۆ office@example.com بنووسە بە بابەتی Marks & attendance و دەقی Is room 3 = room C?. یاریدەدەرەکەی README بەکار بهێنە و بڵێ & و = دەبنە چی.

Show answerوەڵامەکە پیشان بدە
DART
final uri = Uri(
  scheme: 'mailto',
  path: 'office@example.com',
  query: encodeQueryParameters({
    'subject': 'Marks & attendance',
    'body': 'Is room 3 = room C?',
  }),
);

& becomes %26 and = becomes %3D, spaces become %20 and ? becomes %3F, so the separators between the parameters stay clear.

& دەبێتە %26 و = دەبێتە %3D، بۆشاییەکان دەبنە %20 و ? دەبێتە %3F، بۆیە جیاکەرەوەکانی نێوان ڕوون دەمێننەوە.

Exerciseڕاهێنان5. Correct the paper's SMS code٥. کۆدی نامەی کورتی نووسینەکە ڕاست بکەرەوە

The paper writes queryParameters: <String, String>{'body': Uri.encodeComponent(component: smsmessage ?? '')}. List what is wrong and write a correct version.

نووسینەکە دەنووسێت queryParameters: <String, String>{'body': Uri.encodeComponent(component: smsmessage ?? '')}. ئەوەی هەڵەیە ڕیز بکە و وەشانێکی دروست بنووسە.

Show answerوەڵامەکە پیشان بدە
  • component: was the editor's parameter hint in the screenshot, not code: encodeComponent takes one positional argument.
  • encodeComponent inside queryParameters encodes the message twice (%25D8…).
  • component: ئاماژەی دەستکاریکەرەکە بوو لە وێنەکەدا، نەک کۆد: encodeComponent یەک شوێنی وەردەگرێت.
  • encodeComponent لەناو queryParametersـدا نامەکە دوو جار دەگۆڕێت (%25D8…).
DART
final uri = Uri(
  scheme: 'sms',
  path: '07500000000',
  query: 'body=${Uri.encodeComponent(smsmessage ?? '')}',
);
Quick checkپشکنینی خێراWhich part of mailto:teacher@example.com?subject=Hi is the path?کام بەشی mailto:teacher@example.com?subject=Hi ؟

Summaryپوختە

  • Uri is a class of dart:core. Build one with Uri.parse(text), Uri(scheme:, host:, path:, query:, queryParameters:, fragment:) or Uri.https(host, path, map).
  • The website name is the host, not the path; otherwise you get https:www.example.com.
  • Uri.encodeComponent encodes one piece of text: a Kurdish letter → two %XX codes, a space → %20; ! stays.
  • Encode once. queryParameters already encodes, so never put encodeComponent inside it.
  • For sms: and mailto:, use query: with encodeComponent or the README's encodeQueryParameters helper (%20, not +).
  • Values in queryParameters must be strings (or lists of strings).
  • Uri dart:coreـە. بە Uri.parse(text)، Uri(scheme:, host:, path:, query:, queryParameters:, fragment:) یان Uri.https(host, path, map) دروستی بکە.
  • ناوی ، نەک ؛ ئەگەرنا https:www.example.com وەردەگریت.
  • Uri.encodeComponent یەک پارچە دەق دەگۆڕێت: پیتێکی کوردی ← دوو کۆدی %XX، بۆشایی ← %20؛ ! وەک خۆی دەمێنێتەوە.
  • یەک جار بیگۆڕە. queryParameters خۆی دەگۆڕێت، بۆیە هەرگیز encodeComponent مەخەرە ناوییەوە.
  • بۆ sms: و mailto:، query: بە encodeComponent یان یاریدەدەری encodeQueryParametersـی README بەکار بهێنە (%20، نەک +).
  • نرخەکانی queryParameters دەبێت بن (یان لیستی ).
Review questionsپرسیارەکانی پێداچوونەوە
What does %2520 in a link tell you?%2520 لە بەستەرێکدا چیت پێ دەڵێت؟

The text was encoded twice: %25 is an encoded %, so %20 was encoded again.

دەقەکە دوو جار گۆڕدراوە: %25 %ـێکی گۆڕدراوە، بۆیە %20 دووبارە گۆڕدراوە.

How does the SMS body «سڵاو! موحەممەد!» look after Uri.encodeComponent?دەقی نامەی کورتی «سڵاو! موحەممەد!» دوای Uri.encodeComponent چۆن دەردەکەوێت؟

%D8%B3%DA%B5%D8%A7%D9%88!%20%D9%85%D9%88%D8%AD%DB%95%D9%85%D9%85%DB%95%D8%AF!

%D8%B3%DA%B5%D8%A7%D9%88!%20%D9%85%D9%88%D8%AD%DB%95%D9%85%D9%85%DB%95%D8%AF!

Why is query: safer than queryParameters: for sms: links?بۆچی query: بۆ بەستەرەکانی sms: سەلامەتترە لە queryParameters:؟

queryParameters writes spaces as +, which some messaging apps show as a plus sign; with query: and encodeComponent the spaces are %20.

queryParameters بۆشایی وەک + دەنووسێت کە هەندێک نامە وەک نیشانەی کۆ پیشانی دەدەن؛ بە query: و encodeComponent بۆشاییەکان %20ـن.

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

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

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

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

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

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

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