[An den Docks] Flattern. Teil 1. Für Android-Entwickler

Flutter hat bereits viele Artikel geschrieben. Mit jedem Monat wird es beliebter. Deshalb habe ich mich entschlossen, die offizielle Flutter-Dokumentation in einem übersichtlichen Q & A-Format zu interpretieren. Ich denke, viele, wie ich, haben nicht genug Freizeit, um die Dokumentation des Frameworks, mit dem sie immer noch nicht arbeiten, im Detail zu studieren.

Wenn Sie verstehen möchten, wofür dieses Framework gut ist, und bewerten möchten, wie viel Aufwand Sie unternehmen müssen, um es zu verwenden, sind Sie bei cat willkommen.



Inhalt:


  1. Ansichten

  2. Absichten

  3. Async ui

  4. Projektstruktur und Ressourcen

  5. Aktivitäten & Fragmente

  6. Layouts

  7. Gesten- und Berührungsereignisbehandlung.

  8. Listenansichten & Adapter

  9. Mit Text arbeiten

  10. Eingabeformular

  11. Flatter Plugins

  12. Themen

  13. Datenbanken und lokaler Speicher

  14. Benachrichtigungen


Ansichten


Frage:


Was entspricht View in Flutter?

Die Antwort lautet:


Widget

Unterschiede:


Ansicht - in der Tat, was auf dem Bildschirm angezeigt wird. Invalidate () wird aufgerufen, um die Änderungen anzuzeigen.

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

Weitere Informationen:


Beim Start auf Android selbst befindet sich View unter der Haube von Widget. Flutter enthält die Material Components Library. Es enthält Widgets, die Richtlinien für das Materialdesign 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 besteht im Rendering-Status.

Unterschiede:


StatelessWidget hat einen unveränderlichen Zustand. 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 hat 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 die XML-Layoutdatei?

Die Antwort lautet:


Flutter hat kein XML-Layout für Bildschirme. Alles, was im Widget-Baum direkt im Code gesetzt ist.

Ein Beispiel:


 @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Sample App"), ), body: Center( child: MaterialButton( onPressed: () {}, 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 füge ich eine Komponente im Layout hinzu oder entferne sie, während die Anwendung ausgeführt wird?

Die Antwort lautet:


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

Unterschiede:


Unter Android können Sie in einer ViewGroup addView () oder removeView () ausführen. In Flutter ist das nicht möglich, weil Widgets bleiben unverändert. Nur ihr Zustand kann sich ändern.

Ein Beispiel:


So ändern Sie Text in Button, indem Sie auf FloatingActionButton klicken.

 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> { //     bool toggle = true; void _toggle() { setState(() { toggle = !toggle; }); } _getToggleChild() { if (toggle) { return Text('Toggle One'); } else { return MaterialButton(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 Nachkomme der abstrakten Klasse Animation <T> ist . Zusätzlich zum Starten der Animation kann er sie anhalten, zurückspulen, anhalten und in die entgegengesetzte Richtung abspielen. Funktioniert mit Ticker , der eine Neuzeichnung des Bildschirms meldet.

Unterschiede:


Unter Android können Sie mit animate () Animationen in XML erstellen oder die Ansicht animieren. In Flutter muss die Animation mit dem AnimationController in Code geschrieben werden.

Weitere Informationen:


Weitere Informationen finden Sie in den Widgets Animation & Motion , im Tutorial Animationen und in der Animationsübersicht .

Ein Beispiel:


Animation des Flutter-Logos verblassen.

 import 'package:flutter/material.dart'; void main() { runApp(FadeAppTest()); } class FadeAppTest 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() { super.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(); }, ), ); } } 

Frage:


Wie benutzt man Canvas ?

Die Antwort lautet:


Android und Flutter haben dieselbe API für Canvas wie Sie verwenden den gleichen Low-Level- Skia- Motor.

Unterschiede:


Nein.

Weitere Informationen:


Flutter hat zwei Klassen zum Zeichnen auf Leinwand - CustomPaint und CustomPainter . Der zweite implementiert Ihren Rendering-Algorithmus.

Lesen Sie hier mehr: StackOverflow

Ein Beispiel:


 import 'package:flutter/material.dart'; void main() => runApp(MaterialApp(home: DemoApp())); class DemoApp extends StatelessWidget { Widget build(BuildContext context) => Scaffold(body: Signature()); } 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), ); } } 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; } 

Frage:


Wie erstelle ich benutzerdefinierte Widgets?

Die Antwort lautet:


Erstellen Sie Widgets in einem (anstelle der Vererbung).

Unterschiede:


In Android können wir von der Ansicht, an der wir interessiert sind, erben und unsere eigene Logik hinzufügen. In Flutter ähnelt dies einer ViewGroup, nur das Widget wird immer von StatelessWidget oder StatefulWidget geerbt. Das heißt, Sie müssen ein neues Widget erstellen und die Widgets verwenden, die Sie als Parameter oder Felder benötigen.

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

Absichten


Frage:


Was ist das Analogon von Intent in Flutter?

Die Antwort lautet:


Er ist nicht da. Zum Navigieren zwischen Bildschirmen werden die Klassen Navigator und Route verwendet.

Um mit externen Komponenten (z. B. einer Kamera oder einer Dateiauswahl) zu interagieren, können Sie auf jeder Plattform Plugins oder native Integration verwenden. Weitere Informationen zur nativen Integration: Entwickeln von Paketen und Plugins .

Unterschiede:


Flattern hat keine Aktivität und kein Fragment. Es gibt Navigator (Navigator) und Routen (Routen). Die Flutter-Anwendung ähnelt einer Einzelaktivitätsanwendung, bei der verschiedene Bildschirme unterschiedliche Fragmente darstellen und der FragmentManager sie steuert. Navigator ähnelt im Prinzip dem FragmentManager. Es kann Push () oder Pop () zu der von Ihnen angegebenen Route ausführen. Route ist eine Art Fragment, aber in Flutter ist es üblich, es mit einem Bildschirm oder einer Seite zu vergleichen.

In Android beschreiben wir alle Aktivitäten, zwischen denen wir in AndroidManifest.xml navigieren können.

Flattern hat zwei Möglichkeiten:

  • Beschreiben einer Karte mit dem Namen Route (MaterialApp)
  • Navigieren Sie direkt zu Route (WidgetApp).

Ein Beispiel:


 void main() { runApp(MaterialApp( 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 verarbeite ich Absichten aus anderen Anwendungen?

Die Antwort lautet:


Interaktion mit der Android-Ebene der Anwendung über den MethodChannel .

Ein Beispiel:


Wir schreiben Intent-Filter in AndroidManifest.xml:

 <activity android:name=".MainActivity" android:launchMode="singleTop" android:theme="@style/LaunchTheme" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection" android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize"> <!-- ... --> <intent-filter> <action android:name="android.intent.action.SEND" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="text/plain" /> </intent-filter> </activity> 

Wir verarbeiten die Absicht in MainActivity und rufen den Code vom Flutter über den MethodChannel auf:

 package com.example.shared; import android.content.Intent; import android.os.Bundle; import java.nio.ByteBuffer; import io.flutter.app.FlutterActivity; import io.flutter.plugin.common.ActivityLifecycleListener; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; import io.flutter.plugins.GeneratedPluginRegistrant; public class MainActivity extends FlutterActivity { private String sharedText; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); GeneratedPluginRegistrant.registerWith(this); Intent intent = getIntent(); String action = intent.getAction(); String type = intent.getType(); if (Intent.ACTION_SEND.equals(action) && type != null) { if ("text/plain".equals(type)) { handleSendText(intent); // Handle text being sent } } new MethodChannel(getFlutterView(), "app.channel.shared.data").setMethodCallHandler( new MethodCallHandler() { @Override public void onMethodCall(MethodCall call, MethodChannel.Result result) { if (call.method.contentEquals("getSharedText")) { result.success(sharedText); sharedText = null; } } }); } void handleSendText(Intent intent) { sharedText = intent.getStringExtra(Intent.EXTRA_TEXT); } } 

Wir fordern Daten an, wenn das Widget gezeichnet wird:

 import 'package:flutter/material.dart'; import 'package:flutter/services.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 Shared App Handler', 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> { static const platform = const MethodChannel('app.channel.shared.data'); String dataShared = "No data"; @override void initState() { super.initState(); getSharedText(); } @override Widget build(BuildContext context) { return Scaffold(body: Center(child: Text(dataShared))); } getSharedText() async { var sharedData = await platform.invokeMethod("getSharedText"); if (sharedData != null) { setState(() { dataShared = sharedData; }); } } } 

Frage:


Was ist das Analogon von startActivityForResult () ?

Die Antwort lautet:


Das Schlüsselwort await und das Ergebnis einer Future-Klasse .

Unterschiede:


Nach dem Aufruf von startActivityForResult () in Android müssen wir die Verarbeitung in onActivityResult () implementieren. Flutter muss nichts implementieren, weil Die Navigatormethode push () gibt ein Future-Objekt zurück.

Ein Beispiel:


 Map coordinates = await Navigator.of(context).pushNamed('/location'); 

Und wenn wir die Koordinaten auf dem Bildschirm '/ location' haben, machen Sie pop ():

 Navigator.of(context).pop({"lat":43.821757,"long":-79.226392}); 

Async ui


Frage:


Was ist das Analogon von runOnUiThread () in Flutter?

Die Antwort lautet:


Dart implementiert ein Single-Threaded-Ausführungsmodell, das auf Isolates ausgeführt wird . Verwenden Sie für die asynchrone Ausführung async / await, mit dem Sie möglicherweise aus C # -, JavaScript- oder Kotlin-Coroutinen vertraut sind.

Ein Beispiel:


Erfüllen der Anforderung und Zurückgeben des Ergebnisses für die 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 empfangen wird, müssen Sie die Methode setState () aufrufen , um den Widget-Baum 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 führe ich Code in einem Hintergrundthread aus?

Die Antwort lautet:


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

Unterschiede:


Unter Android können Sie AsyncTask sofort verwenden. Sie müssen onPreExecute () , doInBackground () , onPostExecute () darin implementieren. In Flutter "out of the box" müssen Sie nur async / await verwenden, Dart kümmert sich um den Rest.

Ein Beispiel:


Hier wird die dataLoader () -Methode isoliert. Sie können isoliert umfangreiche Vorgänge ausführen, z. B. das Analysieren 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:


Was ist OkHttps Flutter-Äquivalent?

Die Antwort lautet:


Flutter hat ein eigenes HTTP-Paket .

Weitere Informationen:


Bisher wurden nicht alle OkHttp-Funktionen im HTTP-Paket implementiert, sodass viele der fehlenden Funktionen abstrahiert sind und Sie sie bei Bedarf selbst implementieren können.

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 Anforderung auszuführen, rufen Sie wait in der asynchronen 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 kann man Fortschritte zeigen?

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 Android haben Ressourcen einen res-Ordner und Assets. Flutter hat nur Vermögenswerte. Der Assets-Ordner kann sich an einer beliebigen Stelle im Projekt befinden. Schreiben Sie den Pfad in die Datei pubspec.yaml.

Weitere Informationen:


Vergleich der Größe von Grafikressourcen in Android und Flutter.
Android Density QualifierFlatterpixelverhältnis
ldpi0,75x
mdpi1,0x
hdpi1,5x
xhdpi2,0x
xxhdpi3.0x
xxxhdpi4.0x
Flutter verwendet AssetManager oder spezielle Klassen, die mit Asset beginnen, um Ressourcen im Code zu verwenden.

Ein Beispiel:


AssetManager:

 val flutterAssetStream = assetManager.open("flutter_assets/assets/my_flutter_asset.png") 

Ressourcenstandort:

 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.jpeg 

Verwenden von AssetImage :

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

Asset direkt verwenden:

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

Frage:


Wo können Zeichenfolgen gespeichert werden? Wie lokalisiere ich sie?

Die Antwort lautet:


In statischen Feldern speichern. Lokalisieren Sie mit dem intl-Paket .

Ein Beispiel:


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

Frage:


Was ist das Analogon der Gradle-Datei? Wie füge ich Abhängigkeiten hinzu?

Die Antwort lautet:


pubspec.yaml.

Weitere Informationen:


Flutter delegiert die Assembly an native Android- und iOS-Builder. Sehen Sie sich eine Liste aller beliebten Bibliotheken für Flutter at Pub an .

Aktivitäten & Fragmente


Frage:


Was ist das Äquivalent von Aktivität und Fragment in Flattern?

Die Antwort lautet:


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

Weitere Informationen:


Flutter für Android-Entwickler: So entwerfen Sie eine Aktivitäts-Benutzeroberfläche in Flutter .

Frage:


Wie gehe ich mit Lebenszyklusereignissen um?

Die Antwort lautet:


Verwenden der WidgetsBinding- und didChangeAppLifecycleState () -Methode.

Weitere Informationen:


Flutter verwendet FlutterActivity im nativen Code, und die Flutter-Engine macht Änderungen des Verarbeitungsstatus so unauffällig wie möglich. Wenn Sie jedoch je nach Zustand noch etwas arbeiten müssen, ist der Lebenszyklus etwas anders:

  • inaktiv - diese Methode ist nur in iOS, in Android gibt es kein Analogon;
  • pausiert - ähnlich wie onPause () in Android;
  • wieder aufgenommen - ähnlich wie bei onPostResume () in Android;
  • Aussetzen - ähnlich wie bei onStop in Android gibt es in iOS kein Analogon.

Dies wird in der AppLifecycleStatus-Dokumentation ausführlicher 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 Analogon von LinearLayout ?

Die Antwort lautet:


Zeile für horizontal, Spalte für vertikal.

Weitere Informationen:


Flutter für Android-Entwickler: Wie entwerfe ich LinearLayout in Flutter?

Ein Beispiel:


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

Frage:


Was entspricht RelativeLayout ?

Die Antwort lautet:


Widget- Stapel .

Weitere Details:

Stapelüberlauf

Frage:


Was ist das Analogon von ScrollView ?

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

Frage:


Wie gehe ich mit Übergängen zwischen Hoch- und Querformat um?

Die Antwort lautet:


FlutterView verarbeitet Flips, wenn AndroidManifest.xml enthält
android: configChanges = "Ausrichtung | Bildschirmgröße"

Gesten- und Berührungsereignisbehandlung


Frage:


Wie füge ich onClick Listener für Widget in Flutter hinzu?

Die Antwort lautet:


Wenn das Widget Klicks unterstützt, klicken Sie auf 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



Vertikaler Widerstand



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

Listenansichten & Adapter


Frage:


Was ist das Analogon von ListView in Flutter?

Die Antwort lautet:


ListView

Unterschiede:


Flutter muss nicht über das Bereinigen und Wiederverwenden von Elementen nachdenken (was ListView / RecyclerView in Android mithilfe des ViewHolder-Musters tut).

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:


Wie finde ich heraus, auf welches Element geklickt wurde?

Die Antwort lautet:


Einschließen eines Elements in einen GestureDetector .

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(GestureDetector( child: Padding( padding: EdgeInsets.all(10.0), child: Text("Row $i")), onTap: () { print('row tapped'); }, )); } return widgets; } } 

Frage:


Wie aktualisiere ich eine ListView dynamisch ?

Die Antwort lautet:


Wenn Sie einen kleinen Datensatz haben, kann dies über setState () erfolgen . Wenn der Datensatz groß ist, dann über ListView.Builder , ein Analogon zu RecyclerView .

Ein Beispiel:


Mit setState () :

 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 = <Widget>[]; @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'); }); }, ); } } 

Verwenden von ListView.Builder :

 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 = <Widget>[]; @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'); }); }, ); } } 

Mit Text arbeiten


Frage:


Wie verwende ich benutzerdefinierte Schriftarten?

Die Antwort lautet:


Sie müssen nur die Schriftartdatei in den Ordner legen (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 gestalte ich Text-Widgets?

Die Antwort lautet:


Parameter verwenden:

  • Farbe;
  • Dekoration;
  • Dekoration Farbe;
  • DekorationStyle;
  • fontFamily;
  • fontSize;
  • fontStyle;
  • fontWeight;
  • hashCode;
  • height;
  • inherit;
  • letterSpacing;
  • textBaseline;
  • wordSpacing.


: Retrieve the value of a text field .

:


hint TextInput ?

:


InputDecoration , .

Ein Beispiel:


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

:


?

:


InputDecoration .

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

Flutter


:


GPS?

:


geolocator .

:


?

:


image_picker .

:


Facebook?

:


flutter_facebook_login .

:


Firebase?

:


Firebase Flutter first party plugins .


:


() ?

:


Flutter EventBus . : developing packages and plugins .

:


NDK?

:


NDK- Flutter. Flutter .

Themes


:


(Theme) ?

:


MaterialApp WidgetApp .

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


:


Shared Preferences?

:


Shared_Preferences plugin ( NSUserDefaults iOS ).

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

:


SQLite Flutter?

:


SQFlite .


:


push-?

:


Firebase_Messaging .

Fazit


. , , . « » . «-» . , ? 2016 Kotlin, - 2017. , , . , .
2016 Flutter Dart. , 2018 . . ! , , , . ( Google Fuchsia , , , Flutter ). — ! , — . Das ist alles für mich. Google Play!

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


All Articles