[Di dermaga] Berkibar. Bagian 2. Untuk pengembang iOS

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 Android
Bergetar. Bagian 2. Untuk pengembang iOS
Bergetar. Bagian 3. Untuk Pengembang Asli Bereaksi
Bergetar. Bagian 4. Untuk pengembang web
Bergetar. Bagian 5. Untuk Pengembang Xamarin

Konten:


  1. Tampilan

  2. Navigasi

  3. Threading & asinkronisitas

  4. Struktur dan Sumber Daya Proyek

  5. ViewControllers

  6. Tata letak

  7. Gerakan dan penanganan acara sentuh

  8. Penataan gaya aplikasi

  9. Formulir input

  10. Flutter Plugins

  11. Database dan penyimpanan lokal

  12. Notifikasi



Tampilan


Pertanyaan:


Apakah analog UIView dalam Flutter?

Jawabannya adalah:


Widget

Perbedaan:


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 { //     . @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> { //   String textToShow = "  Flutter"; void _updateText() { setState(() { //   textToShow = "Flutter !"; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Sample App"), ), body: Center(child: Text(textToShow)), floatingActionButton: FloatingActionButton( onPressed: _updateText, tooltip: ' ', child: Icon(Icons.update), ), ); } } 

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 { // This widget is the root of your application. @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> { // Default value for toggle bool toggle = true; void _toggle() { setState(() { toggle = !toggle; }); } _getToggleChild() { if (toggle) { return Text('Toggle One'); } else { return CupertinoButton( onPressed: () {}, child: Text('Toggle Two'), ); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Sample App"), ), body: Center( child: _getToggleChild(), ), floatingActionButton: FloatingActionButton( onPressed: _toggle, tooltip: 'Update Text', child: Icon(Icons.update), ), ); } } 

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 { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Fade Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: MyFadeTest(title: 'Fade Demo'), ); } } class MyFadeTest extends StatefulWidget { MyFadeTest({Key key, this.title}) : super(key: key); final String title; @override _MyFadeTest createState() => _MyFadeTest(); } class _MyFadeTest extends State<MyFadeTest> with TickerProviderStateMixin { AnimationController controller; CurvedAnimation curve; @override void initState() { controller = AnimationController(duration: const Duration(milliseconds: 2000), vsync: this); curve = CurvedAnimation(parent: controller, curve: Curves.easeIn); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Center( child: Container( child: FadeTransition( opacity: curve, child: FlutterLogo( size: 100.0, ) ) ) ), floatingActionButton: FloatingActionButton( tooltip: 'Fade', child: Icon(Icons.brush), onPressed: () { controller.forward(); }, ), ); } @override dispose() { controller.dispose(); super.dispose(); } } 

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: StackOverflow

Contoh:


 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(), // becomes the route named '/' routes: <String, WidgetBuilder> { '/a': (BuildContext context) => MyPage(title: 'page A'), '/b': (BuildContext context) => MyPage(title: 'page B'), '/c': (BuildContext context) => MyPage(title: 'page C'), }, )); } Navigator.of(context).pushNamed('/b'); 

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); // The 'echo' isolate sends its SendPort as the first message SendPort sendPort = await receivePort.first; List msg = await sendReceive(sendPort, "https://jsonplaceholder.typicode.com/posts"); setState(() { widgets = msg; }); } // The entry point for the isolate static dataLoader(SendPort sendPort) async { // Open the ReceivePort for incoming messages. ReceivePort port = ReceivePort(); // Notify any other isolates what port this isolate listens to. sendPort.send(port.sendPort); await for (var msg in port) { String data = msg[0]; SendPort replyTo = msg[1]; String dataURL = data; http.Response response = await http.get(dataURL); // Lots of JSON to parse replyTo.send(json.decode(response.body)); } } Future sendReceive(SendPort port, msg) { ReceivePort response = ReceivePort(); port.send([msg, response.sendPort]); return response.first; }   : import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'dart:async'; import 'dart:isolate'; 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() { if (widgets.length == 0) { return true; } return false; } 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 { ReceivePort receivePort = ReceivePort(); await Isolate.spawn(dataLoader, receivePort.sendPort); // The 'echo' isolate sends its SendPort as the first message SendPort sendPort = await receivePort.first; List msg = await sendReceive(sendPort, "https://jsonplaceholder.typicode.com/posts"); setState(() { widgets = msg; }); } // the entry point for the isolate static dataLoader(SendPort sendPort) async { // Open the ReceivePort for incoming messages. ReceivePort port = ReceivePort(); // Notify any other isolates what port this isolate listens to. sendPort.send(port.sendPort); await for (var msg in port) { String data = msg[0]; SendPort replyTo = msg[1]; String dataURL = data; http.Response response = await http.get(dataURL); // Lots of JSON to parse replyTo.send(json.decode(response.body)); } } Future sendReceive(SendPort port, msg) { ReceivePort response = ReceivePort(); port.send([msg, response.sendPort]); return response.first; } } 

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:


ListView

Contoh:


 import 'package:flutter/material.dart'; void main() { runApp(SampleApp()); } class SampleApp extends StatelessWidget { // This widget is the root of your application. @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> { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Sample App"), ), body: ListView(children: _getListData()), ); } _getListData() { List<Widget> widgets = []; for (int i = 0; i < 100; i++) { widgets.add(Padding(padding: EdgeInsets.all(10.0), child: Text("Row $i"))); } return widgets; } } 

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 { // This widget is the root of your application. @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(); for (int i = 0; i < 100; i++) { widgets.add(getRow(i)); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Sample App"), ), body: ListView(children: widgets), ); } Widget getRow(int i) { return GestureDetector( child: Padding( padding: EdgeInsets.all(10.0), child: Text("Row $i"), ), onTap: () { setState(() { widgets = List.from(widgets); widgets.add(getRow(widgets.length + 1)); print('row $i'); }); }, ); } } 


Informasi tambahan:


Untuk membuat daftar, disarankan untuk menggunakan ListView.Builder .

Contoh:


 import 'package:flutter/material.dart'; void main() { runApp(SampleApp()); } class SampleApp extends StatelessWidget { // This widget is the root of your application. @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(); for (int i = 0; i < 100; i++) { widgets.add(getRow(i)); } } @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 GestureDetector( child: Padding( padding: EdgeInsets.all(10.0), child: Text("Row $i"), ), onTap: () { setState(() { widgets.add(getRow(widgets.length + 1)); print('row $i'); }); }, ); } } 


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> { // Create a text controller and use it to retrieve the current value. // of the TextField! final myController = TextEditingController(); @override void dispose() { // Clean up the controller when disposing of the Widget. myController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Retrieve Text Input'), ), body: Padding( padding: const EdgeInsets.all(16.0), child: TextField( controller: myController, ), ), floatingActionButton: FloatingActionButton( // When the user presses the button, show an alert dialog with the // text the user has typed into our text field. onPressed: () { return showDialog( context: context, builder: (context) { return AlertDialog( // Retrieve the text the user has typed in using our // TextEditingController content: Text(myController.text), ); }, ); }, tooltip: 'Show me the value!', child: Icon(Icons.text_fields), ), ); } } 


: 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 { // This widget is the root of your application. @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> { String _errorText; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Sample App"), ), body: Center( child: TextField( onSubmitted: (String text) { setState(() { if (!isEmail(text)) { _errorText = 'Error: This is not an email'; } else { _errorText = null; } }); }, decoration: InputDecoration(hintText: "This is a hint", errorText: _getErrorText()), ), ), ); } _getErrorText() { return _errorText; } bool isEmail(String emailString) { String emailRegexp = r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$'; RegExp regExp = RegExp(emailRegexp); return regExp.hasMatch(emailString); } } 

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!

Source: https://habr.com/ru/post/id477182/


All Articles