[An den Docks] Flattern. Teil 2. Für iOS-Entwickler

Nach einer langen Pause werde ich weiter über das beliebte Flutter-Framework in einem Frage-Antwort-Format sprechen. Den ersten Artikel für Android-Entwickler finden Sie hier . Heute wird es nützliches Material für Entwickler für iOS geben.

Wenn Sie wenig Zeit für ein unabhängiges und gründliches Studium der Dokumentation haben, aber verstehen möchten, was Flutter gut ist und wie es verwendet wird, werfen Sie einen Blick unter die Katze.



Flattern. Teil 1. Für Android-Entwickler
Flattern. Teil 2. Für iOS-Entwickler
Flattern. Teil 3. Für reaktive Entwickler
Flattern. Teil 4. Für Webentwickler
Flattern. Teil 5. Für Xamarin.Forms-Entwickler

Inhalt:


  1. Ansichten

  2. Navigation

  3. Threading & Asynchronität

  4. Projektstruktur und Ressourcen

  5. ViewController

  6. Layouts

  7. Gesten und Touch-Event-Handling

  8. Anwendungs-Styling

  9. Eingabeformular

  10. Flutter Plugins

  11. Datenbanken und lokaler Speicher

  12. Benachrichtigungen



Ansichten


Frage:


Was ist das UIView-Analogon in Flutter?

Die Antwort lautet:


Widget

Unterschiede:


UIView ist eigentlich das, was auf dem Bildschirm angezeigt wird. SetNeedsDisplay () wird aufgerufen, um die Änderungen anzuzeigen.

Widget - eine Beschreibung dessen, was auf dem Bildschirm angezeigt wird. Denn Veränderung wird neu geschaffen.

Weitere Informationen:


Flutter enthält die Cupertino Widgets- Bibliothek. Es enthält Widgets, die Apple Design-Richtlinien implementieren.

Frage:


Wie aktualisiere ich die Anzeige von Widgets?

Die Antwort lautet:


Verwenden von StatefulWidget und seines Status . Flutter hat zwei Arten von Widgets: StatelessWidget und StatefulWidget . Sie funktionieren auf die gleiche Weise, der einzige Unterschied liegt im Rendering-Status.

Unterschiede:


StatelessWidget hat einen unveränderlichen Status. Geeignet zum Anzeigen von Text, Logo usw. Das heißt Sollte sich das Element auf dem Bildschirm während der gesamten Anzeigezeit nicht ändern, passt es zu Ihnen. Es kann auch als Container für Stateful Widgets verwendet werden.

StatefulWidget verfügt über den Status State, in dem Informationen zum aktuellen Status gespeichert werden. Wenn Sie ein Element auf dem Bildschirm ändern möchten, während Sie eine Aktion ausführen (eine Antwort kam vom Server, der Benutzer hat auf eine Schaltfläche geklickt usw.), ist dies Ihre Option.

Ein Beispiel:


1) StatelessWidget - Text

Text( 'I like Flutter!', style: TextStyle(fontWeight: FontWeight.bold), ); 

2) StatefulWidget - Wenn Sie auf die Schaltfläche (FloatingActionButton) klicken, ändert sich der Text im Text-Widget von I Like Flutter zu 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), ), ); } } 

Frage:


Wie erstelle ich einen Bildschirm mit Widgets? Wo ist das Storyboard ?

Die Antwort lautet:


Flutter hat kein Storyboard . Alles, was im Widget-Baum gesetzt wurde, direkt im Code.

Ein Beispiel:


 @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), ), ), ); } 

Alle Standard-Widgets in Flutter können im Widget-Katalog angezeigt werden.

Frage:


Wie kann eine Komponente im Layout hinzugefügt oder entfernt werden, während die Anwendung ausgeführt wird?

Die Antwort lautet:


Durch eine Funktion, die je nach Zustand das gewünschte Widget zurückgibt.

Unterschiede:


Unter iOS können Sie addSubview () oder removeFromSuperview () ausführen. In Flutter ist das nicht möglich, weil Widgets bleiben unverändert. Nur ihr Zustand kann sich ändern.

Ein Beispiel:


Ändern Sie den Text in Button, indem Sie auf FloatingActionButton klicken.

 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), ), ); } } 

Frage:


Wie animiere ich Widgets?

Die Antwort lautet:


Verwenden der AnimationController- Klasse, die der Nachfolger der abstrakten Klasse Animation <T> ist . Er kann die Animation nicht nur starten, sondern auch anhalten, zurückspulen, anhalten und in die entgegengesetzte Richtung abspielen. Funktioniert mit Ticker , der ein Neuzeichnen des Bildschirms meldet.

Unterschiede:


Unter iOS können Sie eine Ansicht mit animate (withDuration: animations :) animieren. In Flutter muss die Animation mit dem AnimationController in Code geschrieben werden.

Weitere Informationen:


Weitere Informationen finden Sie in den Widgets für Animation und Bewegung , im Lernprogramm für Animationen und in der Übersicht über Animationen .

Ein Beispiel:


Überblendungsanimation des Flutter-Logos.

 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(); } } 

Frage:


Wie benutzt man CoreGraphics ?

Die Antwort lautet:


Flutter verwendet die Canvas-API für die Skia- Low-Level-Engine anstelle von CoreGraphics. Android verwendet eine ähnliche Canvas-API.

Weitere Informationen:


Flutter verfügt über zwei Klassen zum Zeichnen auf Leinwand: CustomPaint und CustomPainter . Der zweite implementiert Ihren Rendering-Algorithmus.

Lesen Sie hier mehr: StackOverflow

Ein Beispiel:


 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), ); } } 

Frage:


Wie ändere ich die Transparenz von Widgets?

Die Antwort lautet:


Wickeln Sie das Deckkraft- Widget ein.

Unterschiede:


Unter iOS haben alle Ansichten die Erweiterung .opacity oder .alpha. In Flutter ersetzt diese Option das Wrapper-Widget.

Frage:


Wie erstelle ich benutzerdefinierte Widgets?

Die Antwort lautet:


Verfassen Sie Widgets in einem (anstelle von Vererbung).

Unterschiede:


In iOS können Sie die Ansicht, an der wir interessiert sind, übernehmen und Ihre eigene Logik hinzufügen. In Flutter wird ein Widget immer von StatelessWidget oder StatefulWidget geerbt. Das heißt Sie müssen ein neues Widget erstellen und die benötigten Widgets als Parameter oder Felder verwenden.

Ein Beispiel:


 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"), ); } 

Navigation


Frage:


Wie implementiere ich die Navigation zwischen Bildschirmen in Flutter?

Die Antwort lautet:


Zum Navigieren zwischen Bildschirmen werden die Klassen Navigator und Route verwendet.

Unterschiede:


Flutter kennt keine Konzepte wie UIViewController und UINavigationController. Es gibt Navigator (Navigator) und Routen (Routen). Der Navigator ähnelt im Prinzip dem UINavigationController. Sie können die angegebene Route mit push () oder pop () ansteuern. Route ist eine Art UIViewController, aber in Flutter ist es üblich, es mit einem Bildschirm oder einer Seite zu vergleichen.

Flutter hat zwei Navigationsmethoden:

  • Beschreiben Sie eine Karte mit Routennamen
  • Navigieren Sie direkt zur Route.

Ein Beispiel:


 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'); 

Frage:


Wie navigiere ich zu einer Drittanbieteranwendung?

Die Antwort lautet:


Entweder über MethodChannel mit der iOS-Ebene der Anwendung interagieren oder das URL-Launcher- Plugin verwenden.

Frage:


Wie mache ich Popback in iOS ViewController?

Die Antwort lautet:


Durch Aufrufen von SystemNavigator.pop ().

Weitere Informationen:


SystemNavigator.pop () aus dem Dart-Code ruft in iOS den folgenden Code auf:
 UIViewController* viewController = [UIApplication sharedApplication].keyWindow.rootViewController; if ([viewController isKindOfClass:[UINavigationController class]]) { [((UINavigationController*)viewController) popViewControllerAnimated:NO]; } 

Wenn dies nicht der Fall ist , können Sie Ihre Implementierung über MethodChannel durchführen .

Threading & Asynchronität


Frage:


Wie schreibe ich asynchronen Code in Flutter?

Die Antwort lautet:


Dart implementiert ein Single-Thread-Ausführungsmodell, das auf Isolaten ausgeführt wird . Bei der asynchronen Ausführung wird async / await verwendet, mit dem Sie möglicherweise aus C # -, JavaScript- oder Kotlin-Coroutinen vertraut sind.

Ein Beispiel:


Erfüllung der Anfrage und Rückgabe des Ergebnisses zur Aktualisierung der Benutzeroberfläche:

 loadData() async { String dataURL = "https://jsonplaceholder.typicode.com/posts"; http.Response response = await http.get(dataURL); setState(() { widgets = json.decode(response.body); }); } 

Wenn die Antwort auf die Anforderung eingeht , müssen Sie die setState () -Methode aufrufen, um den Widgetbaum mit den neuen Daten neu zu zeichnen.

Ein Beispiel:


Laden und Aktualisieren von Daten in einer 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); }); } } 

Frage:


Wie wird Code in einem Hintergrundthread ausgeführt?

Die Antwort lautet:


Wie oben erwähnt - Verwenden von Async / Warten und Isolieren (Isolieren).

Unterschiede:


Unter iOS können Sie Operation mit einer möglichen Neudefinition von Methoden verwenden. In Flutter "out of the box" müssen Sie nur async / await verwenden, um den Rest kümmert sich Dart.

Ein Beispiel:


Hier ist die Methode dataLoader () isoliert. Unabhängig davon können Sie umfangreiche Vorgänge ausführen, z. B. das Parsen großer JSONs, die Verschlüsselung, die Bildverarbeitung usw.

 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; } } 

Frage:


Wie mache ich Netzwerkanfragen in Flutter?

Die Antwort lautet:


Flutter hat ein eigenes HTTP-Paket .

Ein Beispiel:


Um das HTTP-Paket zu verwenden, fügen Sie es als Abhängigkeit in pubspec.yaml hinzu:

 dependencies: ... http: ^0.11.3+16 

Um die Anfrage auszuführen, rufen Sie wait in der Async-Funktion http.get () auf:

 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); }); } } 

Frage:


Wie zeige ich Fortschritte?

Die Antwort lautet:


Verwenden des ProgressIndicator- Widgets.

Ein Beispiel:


 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); }); } } 

Projektstruktur und Ressourcen


Frage:


Wo können Ressourcen mit unterschiedlichen Auflösungen gespeichert werden?

Die Antwort lautet:


In Vermögenswerten.

Unterschiede:


In iOS verfügen Grafikressourcen über Images.xcasset, die sich im Assets-Ordner befinden. Flattern hat nur Vermögenswerte. Der Ressourcenordner kann sich an einer beliebigen Stelle im Projekt befinden. Schreiben Sie den Pfad dazu in die Datei pubspec.yaml.

Weitere Informationen:


Die Größe der Grafikressourcen in iOS und Flutter ist identisch und folgt dem auf der Dichte basierenden Format.

Ressourcenposition:

 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 

Pfad in der Datei pubspec.yaml:

 assets: - images/my_icon.png 

Verwenden von AssetImage :

 return AssetImage("images/a_dot_burr.jpeg"); 

Asset direkt nutzen:

 @override Widget build(BuildContext context) { return Image.asset("images/my_image.png"); } 

Frage:


Wo kann man Saiten aufbewahren? Wie kann man sie lokalisieren?

Die Antwort lautet:


In statischen Feldern lagern. Lokalisieren Sie mit intl Paket .

Ein Beispiel:


 class Strings { static String welcomeMessage = "Welcome To Flutter"; } Text(Strings.welcomeMessage) 

Frage:


Was ist das Gegenstück zu CocoaPods? Wie füge ich Abhängigkeiten hinzu?

Die Antwort lautet:


pubspec.yaml.

Weitere Informationen:


Flutter delegiert den Build an native Android- und iOS-Builder. Eine Liste aller gängigen Bibliotheken für Flutter at Pub anzeigen.

ViewController


Frage:


Was entspricht ViewController in Flutter?

Die Antwort lautet:


Alles in Flutter sind Widgets. Die Rolle von ViewController für die Arbeit mit der Benutzeroberfläche übernehmen Widgets. Und die Rolle der Navigation, wie im Abschnitt über die Navigation erwähnt, ist Navigator und Route.

Frage:


Wie gehe ich mit Lebenszyklusereignissen um?

Die Antwort lautet:


Verwenden der WidgetsBinding- und der didChangeAppLifecycleState () -Methode.

Weitere Informationen:


Flutter verwendet FlutterAppDelegate im systemeigenen Code und die Flutter-Engine sorgt dafür, dass Statusänderungen so unauffällig wie möglich behandelt werden. Wenn Sie jedoch je nach Zustand noch etwas arbeiten müssen, ist der Lebenszyklus etwas anders:

  • inaktiv - Die Anwendung ist inaktiv und empfängt keine Benutzereingaben. Dieser Zustand ist nur in iOS, in Android gibt es kein Analogon;
  • angehalten - Die Anwendung ist derzeit für den Benutzer unsichtbar, reagiert nicht auf Benutzereingaben, arbeitet jedoch im Hintergrund.
  • wieder aufgenommen - Die Anwendung ist sichtbar und reagiert auf Benutzereingaben.
  • Aussetzen - Anwendung wird angehalten. Dieser Zustand ist nur in Android, in iOS gibt es kein Analog.

Dies wird ausführlicher in der AppLifecycleStatus-Dokumentation beschrieben .

Ein Beispiel:


 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())); } 

Layouts


Frage:


Was ist das Äquivalent von UITableView und UICollectionView ?

Die Antwort lautet:


ListView

Ein Beispiel:


 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; } } 

Frage:


Woher weiß ich, auf welchen Listeneintrag geklickt wurde?

Die Antwort lautet:


Das Widget, das ein Element der Liste ist, muss den Klick darauf verarbeiten.

Unterschiede:


In iOS ist dafür die separate Methode tableView: didSelectRowAtIndexPath: verantwortlich. In Flutter muss das Listenelement in ein Widget eingeschlossen werden, das Klicks verarbeitet, z. B. den GestureDetector .

Frage:


Wie aktualisiere ich eine ListView dynamisch?

Die Antwort lautet:


Aktualisieren Sie die Datenliste und rufen Sie setState () auf.

Unterschiede:


Unter iOS müssen Sie die Daten aktualisieren und die reloadData-Methode aufrufen. In Flutter wird das Widget nach setState () erneut gezeichnet.

Ein Beispiel:


 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'); }); }, ); } } 


Weitere Informationen:


Zum Erstellen einer Liste wird die Verwendung von ListView.Builder empfohlen.

Ein Beispiel:


 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'); }); }, ); } } 


Frage:


Was ist das Analogon von UIScrollView ?

Die Antwort lautet:


ListView mit Widgets.

Ein Beispiel:


 @override Widget build(BuildContext context) { return ListView( children: <Widget>[ Text('Row One'), Text('Row Two'), Text('Row Three'), Text('Row Four'), ], ); } 


Weitere Informationen:


Weitere Details hier .

Gesten und Touch-Event-Handling


Frage:


Wie man onClick Listener für Widget in Flutter hinzufügt?

Die Antwort lautet:


Wenn das Widget Klicks unterstützt, dann in onPressed (). Wenn nicht, dann in onTap ().

Ein Beispiel:


In onPressed ():

 @override Widget build(BuildContext context) { return RaisedButton( onPressed: () { print("click"); }, child: Text("Button"), ); } 

In onTap ():

 class SampleApp extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( body: Center( child: GestureDetector( child: FlutterLogo( size: 200.0, ), onTap: () { print("tap"); }, ), ), ); } } 

Frage:


Wie gehe ich mit anderen Gesten auf Widgets um?

Die Antwort lautet:


Verwenden des GestureDetector . Sie können die folgenden Aktionen ausführen:

Tippen Sie auf



Doppeltippen



Lang drücken



Vertikal ziehen



Horizontal ziehen



Ein Beispiel:


Verarbeitung auf DoubleTap:

 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(); } }, ), ), ); } } 


Anwendungs-Styling


Frage:


Wie verwende ich das Theme (Theme) in der Anwendung?

Die Antwort lautet:


Verwenden des MaterialApp-Widgets oder der WidgetApp als Stammverzeichnis in der Anwendung.

Ein Beispiel:


 class SampleApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Sample App', theme: ThemeData( primarySwatch: Colors.blue, textSelectionColor: Colors.red ), home: SampleAppPage(), ); } } 


Frage:


Wie verwende ich benutzerdefinierte Schriftarten?

Die Antwort lautet:


Sie müssen nur die Schriftartdatei in den Ordner stellen (denken Sie an den Namen selbst) und den Pfad dazu in pubspec.yaml angeben.

Ein Beispiel:


 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'), ), ), ); } 

Frage:


Wie werden Text-Widgets formatiert?

Die Antwort lautet:


Parameter verwenden:

  • Farbe;
  • Dekoration;
  • decorationColor;
  • decorationStyle;
  • fontFamily;
  • fontSize;
  • fontStyle;
  • fontWeight;
  • hashCode;
  • Höhe;
  • erben;
  • letterSpacing;
  • textBaseline;
  • Wortabstand.


Eingabeformular


Frage:


Wie erhalte ich ein Benutzereingabeergebnis?

Die Antwort lautet:


Verwenden von TextEditingController .

Ein Beispiel:

 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), ), ); } } 


Weitere Informationen finden Sie hier: Abrufen des Werts eines Textfelds .

Frage:


Was ist das Analogon von Hinweis in TextInput ?

Die Antwort lautet:


Der Tooltip kann mit InputDecoration angezeigt und als Konstruktorparameter an das Widget übergeben werden.

Ein Beispiel:


 body: Center( child: TextField( decoration: InputDecoration(hintText: "This is a hint"), ), ) 

Frage:


Wie zeige ich Validierungsfehler an?

Die Antwort lautet:


InputDecoration .

Ein Beispiel:


 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


Frage:


GPS?

Die Antwort lautet:


geolocator .

Frage:


?

Die Antwort lautet:


image_picker .

Frage:


Facebook?

Die Antwort lautet:


flutter_facebook_login .

Frage:


Firebase?

Die Antwort lautet:


Firebase Flutter first party plugins :


Frage:


() ?

Die Antwort lautet:


Flutter EventBus . : developing packages and plugins .


Frage:


UserDefault?

Die Antwort lautet:


Shared_Preferences plugin ( Shared Preferences Android ).

Ein Beispiel:


 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); } 

Frage:


Core Data ?

Die Antwort lautet:


SQFlite .

Benachrichtigungen


Frage:


push-?

Die Antwort lautet:


Firebase_Messaging .

Fazit


. , , . « » . «-» . , ? 2016 Kotlin, - 2017. , , . , .
2016 Flutter Dart. , 2018 . . ! , , , . ( Google Fuchsia , , , Flutter ). — ! , — . . Apple Store!

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


All Articles