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)؛ و بۆچی نرخەکانی دەبێت بن.
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 و نموونەکانی و وانەکە، جێبەجێکراو و ڕاستکراوە.
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. هەر بۆ یەک بەشی . بیر لە فۆرمی پاکەت لە پۆستەخانە بکەرەوە: خانەیەک بۆ شار، یەکێک بۆ شەقام، یەکێک بۆ تێبینی. پاشان ناونیشانەکە بە ڕیزی دروست دەنووسێت، لەگەڵ نیشانەکانی : // ? # لە نێوان بەشەکاندا.
The Uri class: a form with named boxesکلاسی Uri: فۆرمێک بە خانەی ناودارەوە
The named parameters, one by oneپارامیتەرە ناودارەکان، یەک بە یەک
| Parameter | Part of the URIبەشی | Example valueنموونەی نرخ |
|---|---|---|
scheme | The scheme: which kind of link: کام جۆرە | 'https', 'tel', 'sms', 'mailto' |
host | The host: which server (only for links with //): کام (تەنها بۆ ئەو //ـیان تێدایە) | 'www.example.com' |
port | The port number, if not the defaultژمارەی ، ئەگەر بنەڕەتی نەبێت | 8080 |
path | The path: which resource; a phone number or an e-mail address in tel:, sms: and mailto:: کام سەرچاوە؛ ژمارە تەلەفۆن یان لە tel:، sms: و mailto:ـدا | '/courses/flutter', '07500000000' |
pathSegments | The path as a list of pieces; Dart encodes each piece وەک لیستێک لە پارچە؛ هەر پارچەیەک دەگۆڕێت | ['courses', 'flutter'] |
query | The query, as a string you have already encoded، وەک کە پێشتر خۆت گۆڕیوتە | 'body=Hello%20Aram' |
queryParameters | The query, as a Map that Dart encodes for you، وەک Mapـێک کە بۆت دەیگۆڕێت | {'lang': 'ckb'} |
fragment | The fragment: a place inside the page: شوێنێک لەناو لاپەڕەکەدا | 'intro' |
userInfo | A user name before the host (rare)ناوی پێش (کەم) | 'aram' |
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) بەشێک نییە لە ، بەڵکو . شاشەی داهاتوو پیشان دەدات چی ڕوو دەدات کاتێک تێکەڵیان دەکەیت.
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ـێکی گەڕێنراوە دەدات:
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']}');
}scheme: https
host: www.example.com
port: 8080
path: /courses/flutter
query: lang=ckb&page=2
params: {lang: ckb, page: 2}
fragment: intro
lang is ckbExplain 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.
یەک نرخ بە بخوێنەرەوە.
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ـێکی وەردەگرێت. هەر سێکیان هەمان دەدەن:
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);
}https://www.example.com/courses/flutter?lang=ckb https://www.example.com/courses/flutter?lang=ckb https://www.example.com/courses/flutter?lang=ckb true
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 alsoUri.http.Uri.https(host, path, queryParameters)؛Uri.httpـیش هەیە.
The host-or-path bugهەڵەی هۆست یان ڕێڕەو
This is the paper's first example. It wants to open Google's website:
ئەمە یەکەم نموونەی نووسینەکەیە. دەیەوێت Google بکاتەوە:
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 جێبەجێ بکە و چاپی بکە، کێشەکە دەردەکەوێت:
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'));
}wrong: https:www.google.com host: "" path: "www.google.com" right: https://www.google.com host: "www.google.com" path: "" https://www.google.com 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 نە //ـی هەیە و نە ، بۆیە ناتوانێت بیکاتەوە. ڕێک ئەوەی نووسی کە پێی درابوو: و . هەمان تەڵە بەبێ کۆد لە هۆست یان ڕێڕەو؟ ڕوون کراوەتەوە.
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 ئەمە دەکات: پارچەیەک دەق، وەک نامەیەک، دەگۆڕێت بۆ شێوەی ، بۆ ئەوەی بە سەلامەتی وەک یەک بەشی بنێردرێت.
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)}");
}
}%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! سڵاو! موحەممەد! true 'س' -> %D8%B3 'ڵ' -> %DA%B5 'ا' -> %D8%A7 'و' -> %D9%88 '!' -> ! ' ' -> %20
Explain the codeڕوونکردنەوەی کۆدەکە
- Lineدێڕی 4-5
Encode the whole message once.
هەموو نامەکە یەک جار بگۆڕە.
- Lineدێڕی 8-9
Uri.decodeComponentturns it back; nothing is lost.Uri.decodeComponentدەیگەڕێنێتەوە؛ هیچ شتێک ون نابێت.- Lineدێڕی 12-14
The first letters one by one.
یەکەم پیتەکان یەک بە یەک.


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 و - _ . ! ~ * ' ( ) وەک خۆیان دەهێڵێتەوە و هەموو شتێکی تر دەگۆڕێت.
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:
زیاتر لە یەک گۆڕینی هەیە و بە شێوەی بچووک بەڵام گرنگ جیاوازن. ڕاستەقینە:
| Function | Input | Output (dart run) (dart run) | Use it forبۆ چی بەکاری دەهێنیت |
|---|---|---|---|
Uri.encodeComponent | Hello, how are you? | Hello%2C%20how%20are%20you%3F | One part: a message, a subject, a path pieceیەک بەش: نامەیەک، بابەتێک، پارچەیەکی |
Uri.encodeQueryComponent | Hello, how are you? | Hello%2C+how+are+you%3F | HTML-form style queries (space → +) شێوەی فۆرمی HTML (بۆشایی ← +) |
Uri.encodeFull | https://www.example.com/a b?q=x y | https://www.example.com/a%20b?q=x%20y | A whole URI: it keeps / ? & = # تەواو: / ? & = # دەهێڵێتەوە |
Uri.decodeComponent | Hello%2C%20how | Hello, how | Turning 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 بەکار بهێنە.
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 دەردەکەوێت. ئەمە کۆدەکەی تێبینییەکانە، لەگەڵ وەشانەکەی نووسینەکە بۆ هەمان بیرۆکە:
smsMethod() async{
final Uri smsURL = Uri(
scheme: 'sms',
path: '0770893',
queryParameters: <String, String>{
'body': Uri.encodeComponent("My Message"),
},
);
await launchUrl(smsURL);
}sms:0770893?body=My%2520Message
uri = Uri(
scheme: 'sms',
path: '07500000000',
queryParameters: <String, String>{
'body': Uri.encodeComponent(smsmessage ?? ''),
},
);sms:07500000000?body=%25D8%25B3%25DA%25B5%25D8%25A7%25D9%2588%21%2520%25D9%2585%25D9%2588%25D8%25AD%25DB%2595%25D9%2585%25D9%2585%25DB%2595%25D8%25AF%21
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 پیشان دەدات.
Building an sms: linkدروستکردنی بەستەرێکی sms:
Encode once: three ways comparedیەک جار بیگۆڕە: بەراوردی سێ ڕێگا
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');
}A: sms:1234567890?body=Hello%252C%2520how%2520are%2520you%253F B: sms:1234567890?body=Hello%2C+how+are+you%3F C: sms:1234567890?body=Hello%2C%20how%20are%20you%3F
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.
| Wayڕێگا | The message arrives asنامەکە وەک ئەمە دەگات | Verdictبڕیار |
|---|---|---|
A: queryParameters + encodeComponentڕێگای A: queryParameters + encodeComponent | Hello%2C%20how%20are%20you%3F | Wrong: encoded twiceهەڵە: دوو جار گۆڕدراوە |
B: queryParameters onlyڕێگای B: تەنها queryParameters | Hello, how are you? or Hello,+how+are+you? | Risky: some SMS apps show the + signsمەترسیدار: هەندێک نیشانەکانی + پیشان دەدەن |
C: query: + encodeComponentڕێگای C: query: + encodeComponent | Hello, 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 دروست بکە.
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.
نامەکان بکەرەوە؛ شکست ڕابگەیەنە.
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ـێک لە بێت (بۆ کە چەند جار دەردەکەوێت). جێبەجێی بکە:
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()}));
}https://www.example.com/search?tag=dart&tag=flutter Error: type 'int' is not a subtype of type 'Iterable<dynamic>' sms:1234567890?n=5
Explain the codeڕوونکردنەوەی کۆدەکە
- Lineدێڕی 8
A list gives the key twice:
tag=dart&tag=flutter.لیستێک دوو جار دەدات:
tag=dart&tag=flutter.- Lineدێڕی 14
An
intcompiles (the parameter type isdynamic) but fails when the program runs.intـێک کۆمپایل دەبێت (جۆریdynamicـە) بەڵام لە شکست دەهێنێت.- Lineدێڕی 23
Convert numbers with
toString()first.سەرەتا ژمارەکان بە
toString()بگۆڕە.
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:
ناونیشانەکەی وەک و بابەت و دەقەکەی لە هەیە. تێبینییەکان بە دەست دەنووسن:
sendEmail() async{
final Uri emailURL = Uri(
scheme: 'mailto',
path: 'abc@example.com',
query: 'subject=Important Question&body=Dear Students, this ...',
);
await launchUrl(emailURL);
}mailto:abc@example.com?subject=Important%20Question&body=Dear%20Students,%20this%20...
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 دەگۆڕێت و & و = وەک جیاکەرەوە دەهێڵێتەوە. بەڵام مەترسییەکەش ئەوەیە. ئەگەر خودی بابەتەکە & یان =ـی تێدا بێت، ناتوانێت بزانێت ئەمە دەقە:
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);
}mailto:teacher@example.com?subject=Important%20Question&body=Dear%20Students,%20this%20...
mailto:teacher@example.com?subject=Q&A%20session&body=See%20you
{subject: Q, A session: , body: See you}
mailto:teacher@example.com?subject=Q%26A%20session&body=See%20you%20at%2010%3A00%2C%20room%203.
{subject: Q&A session, body: See you at 10:00, room 3.}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 sessioncontains&…بابەتی
Q&A session&ـی تێدایە…- Lineدێڕی 24
…so the app would read the subject as just
Q.…بۆیە بابەتەکە تەنها وەک
Qدەخوێنێتەوە.- Lineدێڕی 30-33
With the helper,
&becomes%26and the subject arrives whole.بە یاریدەدەرەکە،
&دەبێتە%26و بابەتەکە تەواو دەگات.
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، یەک جار گۆڕدراو.
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ـەوە کە کاتێک هیچ شتێک ناتوانێت بکاتەوە پیشان دەدات:
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.httpsputs the name in the host.Uri.httpsناوەکە دەخاتە .
Common mistakesهەڵە باوەکان
| Mistakeهەڵە | What you getچی وەردەگریت | Fixچاککردن |
|---|---|---|
Uri(scheme: 'https', path: 'www.example.com') | https:www.example.com | host: '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%2520Message | query: 'body=${Uri.encodeComponent(msg)}'query: 'body=${Uri.encodeComponent(msg)}' |
queryParameters for sms:/mailto:queryParameters بۆ sms:/mailto: | body=See+you | query: 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 time | value.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ڕاهێنانەکان
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وەڵامەکە پیشان بدە
final uri = Uri(
scheme: 'sms',
path: '1234567890',
query: 'body=${Uri.encodeComponent('Hello, how are you?')}',
);
print(uri);sms:1234567890?body=Hello%2C%20how%20are%20you%3F
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وەڵامەکە پیشان بدە
// 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');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 «کورد».
هەر جووتێک کۆد یەک پیتە: ک، و، ر، د. دەقەکە «کورد»ـە.
print(Uri.decodeComponent('%DA%A9%D9%88%D8%B1%D8%AF'));کورد
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وەڵامەکە پیشان بدە
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، بۆیە جیاکەرەوەکانی نێوان ڕوون دەمێننەوە.
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:encodeComponenttakes one positional argument.- encodeComponent inside queryParameters encodes the message twice (
%25D8…).
component:ئاماژەی دەستکاریکەرەکە بوو لە وێنەکەدا، نەک کۆد:encodeComponentیەک شوێنی وەردەگرێت.- encodeComponent لەناو queryParametersـدا نامەکە دوو جار دەگۆڕێت (
%25D8…).
final uri = Uri(
scheme: 'sms',
path: '07500000000',
query: 'body=${Uri.encodeComponent(smsmessage ?? '')}',
);Summaryپوختە
Uriis a class ofdart:core. Build one withUri.parse(text),Uri(scheme:, host:, path:, query:, queryParameters:, fragment:)orUri.https(host, path, map).- The website name is the host, not the path; otherwise you get
https:www.example.com. Uri.encodeComponentencodes one piece of text: a Kurdish letter → two%XXcodes, a space →%20;!stays.- Encode once.
queryParametersalready encodes, so never putencodeComponentinside it. - For
sms:andmailto:, usequery:withencodeComponentor the README'sencodeQueryParametershelper (%20, not+). - Values in
queryParametersmust be strings (or lists of strings).
Uridart: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دەبێت بن (یان لیستی ).
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زاراوە سەرەکییەکان
Getting Data from the Internet: HTTP and JSONوەرگرتنی دراوە لە ئینتەرنێت: HTTP و JSON
Related lecturesوانە پەیوەندیدارەکان
References and further readingسەرچاوەکان و خوێندنەوەی زیاتر
This lecture is a first edition. The Kurdish text and terminology are waiting for review by Kurdish IT teachers.ئەم وانەیە چاپی یەکەمە. دەقە کوردییەکە و زاراوەکانی چاوەڕێی پێداچوونەوەی مامۆستایانی کوردی بواری IT ن.