Setelah istirahat panjang, saya akan terus berbicara tentang kerangka kerja Flutter yang populer dalam format tanya-jawab. Anda dapat menemukan artikel pertama untuk pengembang Android di
sini , dan hari ini akan ada materi yang berguna untuk pengembang untuk iOS.
Jika Anda memiliki sedikit waktu untuk studi dokumentasi yang independen dan mendalam, tetapi ingin memahami apa yang baik untuk Flutter dan bagaimana menggunakannya, lihat di bawah Cat.
Bergetar. Bagian 1. Untuk pengembang AndroidBergetar. Bagian 2. Untuk pengembang iOSBergetar. Bagian 3. Untuk Pengembang Asli BereaksiBergetar. Bagian 4. Untuk pengembang webBergetar. Bagian 5. Untuk Pengembang Xamarin
Konten:
- Tampilan
- Navigasi
- Threading & asinkronisitas
- Struktur dan Sumber Daya Proyek
- ViewControllers
- Tata letak
- Gerakan dan penanganan acara sentuh
- Penataan gaya aplikasi
- Formulir input
- Flutter Plugins
- Database dan penyimpanan lokal
- Notifikasi
Tampilan
Pertanyaan:
Apakah
analog UIView dalam Flutter?
Jawabannya adalah:
WidgetPerbedaan:
UIView sebenarnya adalah apa yang akan ada di layar. SetNeedsDisplay () dipanggil untuk menampilkan perubahan.
Widget - deskripsi tentang apa yang akan ada di layar. Untuk perubahan dibuat lagi.
Informasi tambahan:
Flutter termasuk perpustakaan
Cupertino Widgets . Ini berisi widget yang menerapkan
pedoman Desain Apple .
Pertanyaan:
Bagaimana cara memperbarui tampilan widget?
Jawabannya adalah:
Menggunakan
StatefulWidget dan
State -nya. Flutter memiliki 2 jenis widget:
StatelessWidget dan
StatefulWidget . Mereka bekerja dengan cara yang sama, satu-satunya perbedaan adalah dalam keadaan rendering.
Perbedaan:
StatelessWidget memiliki status yang tidak dapat diubah. Cocok untuk menampilkan teks, logo, dll. Yaitu jika elemen pada layar tidak boleh berubah selama seluruh waktu tampilan, maka itu cocok untuk Anda. Ini juga dapat digunakan sebagai wadah untuk widget stateful.
StatefulWidget memiliki status Negara, yang menyimpan informasi tentang keadaan saat ini. Jika Anda ingin mengubah elemen di layar saat melakukan beberapa tindakan (respons datang dari server, pengguna mengklik tombol, dll.) - ini adalah pilihan Anda.
Contoh:
1) StatelessWidget - Teks
Text( 'I like Flutter!', style: TextStyle(fontWeight: FontWeight.bold), );
2) StatefulWidget - ketika Anda mengklik tombol (FloatingActionButton), teks dalam widget Teks berubah dari
I Like Flutter ke
Flutter is Awesome! import 'package:flutter/material.dart'; void main() { runApp(SampleApp()); } class SampleApp extends StatelessWidget {
Pertanyaan:
Bagaimana cara menata layar dengan widget? Di mana
storyboardnya ?
Jawabannya adalah:
Flutter tidak memiliki
Storyboard . Semuanya mengeset di pohon widget langsung di kode.
Contoh:
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Sample App"), ), body: Center( child: CupertinoButton( onPressed: () { setState(() { _pressedCount += 1; }); }, child: Text('Hello'), padding: EdgeInsets.only(left: 10.0, right: 10.0), ), ), ); }
Semua widget default di Flutter dapat dilihat di
katalog widget .
Pertanyaan:
Bagaimana cara menambah atau menghapus komponen dalam tata letak saat aplikasi sedang berjalan?
Jawabannya adalah:
Melalui fungsi yang akan mengembalikan widget yang diinginkan tergantung pada negara.
Perbedaan:
Di iOS, Anda dapat melakukan addSubview () atau menghapusFromSuperview (). Di Flutter itu tidak mungkin karena widget tidak berubah. Hanya kondisinya yang bisa berubah.
Contoh:
Ubah Teks ke Tombol dengan mengklik FloatingActionButton.
class SampleApp extends StatelessWidget {
Pertanyaan:
Bagaimana cara menghidupkan widget?
Jawabannya adalah:
Menggunakan kelas
AnimationController , yang merupakan turunan dari kelas abstrak
Animation <T> . Selain memulai animasi, ia dapat menjeda, memundurkan, berhenti dan memainkannya dengan arah yang berlawanan. Bekerja dengan
Ticker , yang melaporkan redraw layar.
Perbedaan:
Di iOS, Anda dapat menganimasikan tampilan menggunakan animate (withDuration: animations :). Dalam Flutter, animasi harus ditulis dalam kode menggunakan AnimationController.
Informasi tambahan:
Anda dapat mempelajari lebih lanjut di
widget Animasi & Gerak ,
tutorial Animasi, dan
ikhtisar Animasi .
Contoh:
Animasi fade dari logo Flutter.
class SampleApp extends StatelessWidget {
Pertanyaan:
Bagaimana cara menggunakan
CoreGraphics ?
Jawabannya adalah:
Flutter menggunakan Kanvas API pada mesin tingkat rendah
Skia bukan CoreGraphics. Android menggunakan Canvas API yang serupa.
Informasi tambahan:
Flutter memiliki dua kelas untuk menggambar di atas Kanvas:
CustomPaint dan
CustomPainter . Yang kedua mengimplementasikan algoritma rendering Anda.
Baca lebih lanjut di sini:
StackOverflowContoh:
class SignaturePainter extends CustomPainter { SignaturePainter(this.points); final List<Offset> points; void paint(Canvas canvas, Size size) { var paint = Paint() ..color = Colors.black ..strokeCap = StrokeCap.round ..strokeWidth = 5.0; for (int i = 0; i < points.length - 1; i++) { if (points[i] != null && points[i + 1] != null) canvas.drawLine(points[i], points[i + 1], paint); } } bool shouldRepaint(SignaturePainter other) => other.points != points; } class Signature extends StatefulWidget { SignatureState createState() => SignatureState(); } class SignatureState extends State<Signature> { List<Offset> _points = <Offset>[]; Widget build(BuildContext context) { return GestureDetector( onPanUpdate: (DragUpdateDetails details) { setState(() { RenderBox referenceBox = context.findRenderObject(); Offset localPosition = referenceBox.globalToLocal(details.globalPosition); _points = List.from(_points)..add(localPosition); }); }, onPanEnd: (DragEndDetails details) => _points.add(null), child: CustomPaint(painter: SignaturePainter(_points), size: Size.infinite), ); } }
Pertanyaan:
Bagaimana cara mengubah transparansi widget?
Jawabannya adalah:
Bungkus dalam widget
Opacity .
Perbedaan:
Di iOS, semua tampilan memiliki .opacity atau .alpha. Di Flutter, opsi ini menggantikan widget pembungkus.
Pertanyaan:
Bagaimana cara membuat widget khusus?
Jawabannya adalah:
Tulis widget di dalam satu (bukan warisan).
Perbedaan:
Di iOS, Anda dapat mewarisi dari tampilan yang kami minati dan menambahkan logika Anda sendiri. Di Flutter, widget selalu diwarisi dari StatelessWidget atau StatefulWidget. Yaitu Anda perlu membuat widget baru dan menggunakannya seperangkat widget yang Anda butuhkan sebagai parameter atau bidang.
Contoh:
class CustomButton extends StatelessWidget { final String label; CustomButton(this.label); @override Widget build(BuildContext context) { return RaisedButton(onPressed: () {}, child: Text(label)); } } @override Widget build(BuildContext context) { return Center( child: CustomButton("Hello"), ); }
Navigasi
Pertanyaan:
Bagaimana cara menerapkan navigasi antar layar dalam Flutter?
Jawabannya adalah:
Menavigasi antar layar menggunakan kelas
Navigator dan
Rute .
Perbedaan:
Flutter tidak memiliki konsep seperti UIViewController dan UINavigationController. Ada Navigator (Navigator) dan Rute (rute). Navigator mirip dengan UINavigationController pada prinsipnya. Itu dapat melakukan
push () atau
pop () ke rute yang Anda tentukan. Route adalah sejenis UIViewController, tetapi dalam Flutter biasanya membandingkannya dengan layar atau halaman.
Flutter memiliki dua metode navigasi:
- Jelaskan Peta dengan nama Rute
- Navigasi langsung ke Rute.
Contoh:
void main() { runApp(CupertinoApp( home: MyAppHome(),
Pertanyaan:
Bagaimana cara menavigasi ke aplikasi pihak ketiga?
Jawabannya adalah:
Baik berinteraksi dengan lapisan aplikasi
iOS melalui
MethodChannel , atau menggunakan plugin
URL launcher .
Pertanyaan:
Bagaimana cara membuat pop back di iOS ViewController?
Jawabannya adalah:
Dengan memanggil SystemNavigator.pop ().
Informasi tambahan:
SystemNavigator.pop () dari kode Dart memanggil kode berikut di iOS:
UIViewController* viewController = [UIApplication sharedApplication].keyWindow.rootViewController; if ([viewController isKindOfClass:[UINavigationController class]]) { [((UINavigationController*)viewController) popViewControllerAnimated:NO]; }
Jika ini bukan yang Anda butuhkan, maka Anda bisa melakukan implementasi Anda melalui
MethodChannel .
Threading & asinkronisitas
Pertanyaan:
Bagaimana cara menulis kode asinkron dalam Flutter?
Jawabannya adalah:
Dart mengimplementasikan model eksekusi single-threaded yang berjalan pada
Isolates . Eksekusi asinkron menggunakan async / menunggu, yang mungkin Anda kenal dari C #, JavaScript, atau koroutine Kotlin.
Contoh:
Memenuhi permintaan dan mengembalikan hasilnya untuk memperbarui UI:
loadData() async { String dataURL = "https://jsonplaceholder.typicode.com/posts"; http.Response response = await http.get(dataURL); setState(() { widgets = json.decode(response.body); }); }
Ketika respons terhadap permintaan diterima, Anda perlu memanggil metode
setState () untuk menggambar ulang pohon widget dengan data baru.
Contoh:
Memuat dan memperbarui data dalam
ListView :
import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; void main() { runApp(SampleApp()); } class SampleApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Sample App', theme: ThemeData( primarySwatch: Colors.blue, ), home: SampleAppPage(), ); } } class SampleAppPage extends StatefulWidget { SampleAppPage({Key key}) : super(key: key); @override _SampleAppPageState createState() => _SampleAppPageState(); } class _SampleAppPageState extends State<SampleAppPage> { List widgets = []; @override void initState() { super.initState(); loadData(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Sample App"), ), body: ListView.builder( itemCount: widgets.length, itemBuilder: (BuildContext context, int position) { return getRow(position); })); } Widget getRow(int i) { return Padding( padding: EdgeInsets.all(10.0), child: Text("Row ${widgets[i]["title"]}") ); } loadData() async { String dataURL = "https://jsonplaceholder.typicode.com/posts"; http.Response response = await http.get(dataURL); setState(() { widgets = json.decode(response.body); }); } }
Pertanyaan:
Bagaimana cara mengeksekusi kode di utas latar?
Jawabannya adalah:
Seperti disebutkan di atas - menggunakan async / await and isolation (Isolate).
Perbedaan:
Di luar kotak di iOS, Anda dapat menggunakan Operasi dengan kemungkinan penggantian metode. Di Flutter "out of the box" Anda hanya perlu menggunakan async / menunggu, Dart akan mengurus sisanya.
Contoh:
Di sini metode dataLoader () diisolasi. Dalam isolasi, Anda dapat menjalankan operasi berat seperti mem-parsing JSON besar, enkripsi, pemrosesan gambar, dll.
loadData() async { ReceivePort receivePort = ReceivePort(); await Isolate.spawn(dataLoader, receivePort.sendPort);
Pertanyaan:
Bagaimana cara membuat permintaan jaringan di Flutter?
Jawabannya adalah:
Flutter memiliki
paket HTTP sendiri.
Contoh:
Untuk menggunakan paket HTTP, tambahkan itu sebagai dependensi di pubspec.yaml:
dependencies: ... http: ^0.11.3+16
Untuk menjalankan permintaan, panggilan tunggu di fungsi async http.get ():
import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; [...] loadData() async { String dataURL = "https://jsonplaceholder.typicode.com/posts"; http.Response response = await http.get(dataURL); setState(() { widgets = json.decode(response.body); }); } }
Pertanyaan:
Bagaimana cara menunjukkan kemajuan?
Jawabannya adalah:
Menggunakan widget
ProgressIndicator .
Contoh:
import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; void main() { runApp(SampleApp()); } class SampleApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Sample App', theme: ThemeData( primarySwatch: Colors.blue, ), home: SampleAppPage(), ); } } class SampleAppPage extends StatefulWidget { SampleAppPage({Key key}) : super(key: key); @override _SampleAppPageState createState() => _SampleAppPageState(); } class _SampleAppPageState extends State<SampleAppPage> { List widgets = []; @override void initState() { super.initState(); loadData(); } showLoadingDialog() { return widgets.length == 0; } getBody() { if (showLoadingDialog()) { return getProgressDialog(); } else { return getListView(); } } getProgressDialog() { return Center(child: CircularProgressIndicator()); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Sample App"), ), body: getBody()); } ListView getListView() => ListView.builder( itemCount: widgets.length, itemBuilder: (BuildContext context, int position) { return getRow(position); }); Widget getRow(int i) { return Padding(padding: EdgeInsets.all(10.0), child: Text("Row ${widgets[i]["title"]}")); } loadData() async { String dataURL = "https://jsonplaceholder.typicode.com/posts"; http.Response response = await http.get(dataURL); setState(() { widgets = json.decode(response.body); }); } }
Struktur dan Sumber Daya Proyek
Pertanyaan:
Di mana menyimpan sumber daya dari berbagai resolusi?
Jawabannya adalah:
Dalam aset.
Perbedaan:
Di iOS, sumber daya grafis memiliki Images.xcasset, yang terletak di folder aset. Flutter hanya memiliki aset. Folder sumber daya dapat ditemukan di mana saja di dalam proyek, yang paling penting, tulis path untuk itu di file pubspec.yaml.
Informasi tambahan:
Ukuran sumber daya grafis di iOS dan Flutter identik dan mengikuti format berbasis kepadatan.
Lokasi Sumber Daya:
images/my_icon.png // Base: 1.0x image images/2.0x/my_icon.png // 2.0x image images/3.0x/my_icon.png // 3.0x image
Path dalam file pubspec.yaml:
assets: - images/my_icon.png
Menggunakan
AssetImage :
return AssetImage("images/a_dot_burr.jpeg");
Menggunakan aset secara langsung:
@override Widget build(BuildContext context) { return Image.asset("images/my_image.png"); }
Pertanyaan:
Di mana menyimpan string? Bagaimana cara melokalkannya?
Jawabannya adalah:
Simpan di bidang statis. Lokalisasi menggunakan
paket intl .
Contoh:
class Strings { static String welcomeMessage = "Welcome To Flutter"; } Text(Strings.welcomeMessage)
Pertanyaan:
Apa yang menjadi mitra CocoaPods? Bagaimana cara menambahkan dependensi?
Jawabannya adalah:
pubspec.yaml.
Informasi tambahan:
Flutter mendelegasikan build ke pembuat Android dan iOS asli. Lihat daftar semua perpustakaan populer untuk Flutter di
Pub .
ViewControllers
Pertanyaan:
Apa yang setara dengan
ViewController di Flutter?
Jawabannya adalah:
Semua yang ada di Flutter adalah widget. Peran ViewController untuk bekerja dengan UI dimainkan oleh widget. Dan peran navigasi, sebagaimana disebutkan dalam paragraf tentang navigasi, adalah Navigator dan Rute.
Pertanyaan:
Bagaimana cara menangani acara siklus hidup?
Jawabannya adalah:
Menggunakan metode
WidgetsBinding dan
didChangeAppLifecycleState () .
Informasi tambahan:
Flutter menggunakan FlutterAppDelegate dalam kode aslinya, dan mesin Flutter membuat penanganan perubahan status menjadi tidak terlihat mungkin. Tetapi jika Anda masih perlu melakukan beberapa pekerjaan tergantung pada negara, maka siklus hidupnya sedikit berbeda:
- tidak aktif - aplikasi tidak aktif dan tidak menerima input pengguna. Keadaan ini hanya di iOS, di Android tidak ada analog;
- dijeda - aplikasi saat ini tidak terlihat oleh pengguna, tidak menanggapi input pengguna, tetapi bekerja di latar belakang;
- dilanjutkan - aplikasi terlihat dan merespons input pengguna;
- menangguhkan - aplikasi dalam proses berhenti. Keadaan ini hanya di Android, di iOS tidak ada analog.
Ini dijelaskan secara lebih rinci dalam
dokumentasi AppLifecycleStatus .
Contoh:
import 'package:flutter/widgets.dart'; class LifecycleWatcher extends StatefulWidget { @override _LifecycleWatcherState createState() => _LifecycleWatcherState(); } class _LifecycleWatcherState extends State<LifecycleWatcher> with WidgetsBindingObserver { AppLifecycleState _lastLifecycleState; @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); } @override void dispose() { WidgetsBinding.instance.removeObserver(this); super.dispose(); } @override void didChangeAppLifecycleState(AppLifecycleState state) { setState(() { _lastLifecycleState = state; }); } @override Widget build(BuildContext context) { if (_lastLifecycleState == null) return Text('This widget has not observed any lifecycle changes.', textDirection: TextDirection.ltr); return Text('The most recent lifecycle state this widget observed was: $_lastLifecycleState.', textDirection: TextDirection.ltr); } } void main() { runApp(Center(child: LifecycleWatcher())); }
Tata letak
Pertanyaan:
Apa yang setara dengan
UITableView dan
UICollectionView ?
Jawabannya adalah:
ListViewContoh:
import 'package:flutter/material.dart'; void main() { runApp(SampleApp()); } class SampleApp extends StatelessWidget {
Pertanyaan:
Bagaimana saya tahu item daftar mana yang diklik?
Jawabannya adalah:
Widget, yang merupakan elemen dari daftar, harus menangani klik di atasnya.
Perbedaan:
Di iOS, metode tableView terpisah: didSelectRowAtIndexPath: bertanggung jawab untuk ini. Di Flutter, item daftar harus dibungkus dengan widget yang menangani klik, seperti
GestureDetector .
Pertanyaan:
Bagaimana cara memperbarui
ListView secara dinamis?
Jawabannya adalah:
Refresh daftar data dan panggil setState ().
Perbedaan:
Di iOS, Anda perlu memperbarui data dan memanggil metode reloadData. Di Flutter, setelah setState (), widget akan digambar kembali.
Contoh:
import 'package:flutter/material.dart'; void main() { runApp(SampleApp()); } class SampleApp extends StatelessWidget {
Informasi tambahan:
Untuk membuat daftar, disarankan untuk menggunakan
ListView.Builder .
Contoh:
import 'package:flutter/material.dart'; void main() { runApp(SampleApp()); } class SampleApp extends StatelessWidget {
Pertanyaan:
Apa yang dimaksud dengan analog
UIScrollView ?
Jawabannya adalah:
ListView dengan widget.
Contoh:
@override Widget build(BuildContext context) { return ListView( children: <Widget>[ Text('Row One'), Text('Row Two'), Text('Row Three'), Text('Row Four'), ], ); }
Informasi tambahan:
Lebih detail di
sini .
Gerakan dan penanganan acara sentuh
Pertanyaan:
Bagaimana cara menambahkan pendengar onClick untuk widget di Flutter?
Jawabannya adalah:
Jika widget mendukung klik, maka dalam onPressed (). Jika tidak, maka di onTap ().
Contoh:
Di onPressed ():
@override Widget build(BuildContext context) { return RaisedButton( onPressed: () { print("click"); }, child: Text("Button"), ); }
Di onTap ():
class SampleApp extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( body: Center( child: GestureDetector( child: FlutterLogo( size: 200.0, ), onTap: () { print("tap"); }, ), ), ); } }
Pertanyaan:
Bagaimana cara menangani gerakan lain pada widget?
Jawabannya adalah:
Menggunakan
GestureDetector . Mereka dapat menangani tindakan berikut:
Ketuk
Ketuk dua kali
Tekan lama
Seret vertikal
Seret horizontal
Contoh:
Memproses onDoubleTap:
AnimationController controller; CurvedAnimation curve; @override void initState() { controller = AnimationController(duration: const Duration(milliseconds: 2000), vsync: this); curve = CurvedAnimation(parent: controller, curve: Curves.easeIn); } class SampleApp extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( body: Center( child: GestureDetector( child: RotationTransition( turns: curve, child: FlutterLogo( size: 200.0, )), onDoubleTap: () { if (controller.isCompleted) { controller.reverse(); } else { controller.forward(); } }, ), ), ); } }
Penataan gaya aplikasi
Pertanyaan:
Bagaimana cara menggunakan tema (Theme) dalam aplikasi?
Jawabannya adalah:
Menggunakan widget MaterialApp atau WidgetApp sebagai root di aplikasi.
Contoh:
class SampleApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Sample App', theme: ThemeData( primarySwatch: Colors.blue, textSelectionColor: Colors.red ), home: SampleAppPage(), ); } }
Pertanyaan:
Bagaimana cara menggunakan font khusus?
Jawabannya adalah:
Anda hanya perlu meletakkan file font di folder (pikirkan namanya sendiri) dan tunjukkan path ke sana di pubspec.yaml.
Contoh:
fonts: - family: MyCustomFont fonts: - asset: fonts/MyCustomFont.ttf - style: italic
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Sample App"), ), body: Center( child: Text( 'This is a custom font text', style: TextStyle(fontFamily: 'MyCustomFont'), ), ), ); }
Pertanyaan:
Bagaimana cara mendesain widget teks?
Jawabannya adalah:
Menggunakan parameter:
- warna;
- dekorasi;
- warna dekorasi;
- dekorasi Gaya;
- fontFamily;
- fontSize;
- fontStyle;
- fontBerat;
- kode hash;
- tinggi;
- mewarisi;
- spasi surat;
- textBaseline;
- spasi kata.
Formulir input
Pertanyaan:
Bagaimana cara mendapatkan hasil input pengguna?
Jawabannya adalah:
Menggunakan
TextEditingController .
Contoh:
class _MyFormState extends State<MyForm> {
:
Retrieve the value of a text field .
:
hint
TextInput ?
:
InputDecoration , .
Contoh:
body: Center( child: TextField( decoration: InputDecoration(hintText: "This is a hint"), ), )
:
?
:
โ
InputDecoration .
Contoh:
class SampleApp extends StatelessWidget {
Flutter
:
GPS?
:
geolocator .
:
?
:
image_picker .
:
Facebook?
:
flutter_facebook_login .
:
Firebase?
:
Firebase
Flutter first party plugins :
:
() ?
:
Flutter EventBus . :
developing packages and plugins .
:
UserDefault?
:
Shared_Preferences plugin ( Shared Preferences Android ).
Contoh:
import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; void main() { runApp( MaterialApp( home: Scaffold( body: Center( child: RaisedButton( onPressed: _incrementCounter, child: Text('Increment Counter'), ), ), ), ), ); } _incrementCounter() async { SharedPreferences prefs = await SharedPreferences.getInstance(); int counter = (prefs.getInt('counter') ?? 0) + 1; print('Pressed $counter times.'); prefs.setInt('counter', counter); }
:
Core Data ?
:
SQFlite .
:
push-?
:
Firebase_Messaging .
Kesimpulan
. , , . ยซ ยป . ยซ-ยป . , ? 2016 Kotlin, - 2017. , , . , .
2016 Flutter Dart. , 2018 . . ! , , , . ( Google Fuchsia , , , Flutter ). โ ! , โ . . Apple Store!