[Par les quais] Flutter. Partie 1. Pour les développeurs Android

Flutter a déjà écrit de nombreux articles. Chaque mois, il devient de plus en plus populaire. J'ai donc décidé d'interpréter la documentation officielle de Flutter dans un format Q & A concis. Je pense que beaucoup, comme moi, n'ont pas assez de temps libre pour étudier en détail la documentation du cadre avec lequel ils ne fonctionnent toujours pas.

Si vous voulez comprendre à quoi sert ce cadre et évaluer combien d'efforts vous devrez faire pour l'utiliser, bienvenue à cat.



Contenu:


  1. Vues

  2. Intentions

  3. Async ui

  4. Structure et ressources du projet

  5. Activités et fragments

  6. Disposition

  7. Gestes et gestion des événements tactiles.

  8. Vues de liste et adaptateurs

  9. Travailler avec du texte

  10. Formulaire de saisie

  11. Plugins Flutter

  12. Thèmes

  13. Bases de données et stockage local

  14. Notifications


Vues


Question:


Quel est l'équivalent de View in Flutter?

La réponse est:


Widget

Différences:


Voir - en fait, ce qui sera à l'écran. Invalidate () est appelé pour afficher les modifications.

Widget - une description de ce qui sera à l'écran. Car le changement est créé à nouveau.

Information complémentaire:


Lorsqu'il est lancé sur Android lui-même, View est sous le capot de Widget. Flutter comprend la bibliothèque de composants de matériaux . Il contient des widgets qui mettent en œuvre les directives de conception des matériaux .

Question:


Comment mettre à jour l'affichage des widgets?

La réponse est:


Utilisation de StatefulWidget et de son état . Flutter a 2 types de widgets: StatelessWidget et StatefulWidget . Ils fonctionnent de la même manière, la seule différence est dans l'état de rendu.

Différences:


StatelessWidget a un état immuable. Convient pour afficher du texte, un logo, etc. C'est-à-dire si l'élément à l'écran ne doit pas changer pendant toute la durée d'affichage, cela vous convient. Il peut également être utilisé comme conteneur pour les widgets avec état.

StatefulWidget a l'état State, qui stocke des informations sur l'état actuel. Si vous souhaitez modifier un élément à l'écran lors de l'exécution d'une action (une réponse est venue du serveur, l'utilisateur a cliqué sur un bouton, etc.) - c'est votre option.

Un exemple:


1) StatelessWidget - Texte

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

2) StatefulWidget - lorsque vous cliquez sur le bouton (FloatingActionButton), le texte du widget Texte passe de «J'aime Flutter» à «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), ), ); } } 

Question:


Comment mettre en page un écran avec des widgets? Où est le fichier de disposition XML?

La réponse est:


Flutter n'a pas de disposition XML pour les écrans. Tout est composé dans l'arborescence des widgets directement dans le code.

Un exemple:


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

Tous les widgets par défaut de Flutter peuvent être affichés dans le catalogue de widgets .

Question:


Comment ajouter ou supprimer un composant dans la mise en page pendant que l'application est en cours d'exécution?

La réponse est:


Grâce à une fonction qui renverra le widget souhaité en fonction de l'état.

Différences:


Sur Android, vous pouvez faire addView () ou removeView () dans un ViewGroup. Dans Flutter, vous ne pouvez pas les widgets sont inchangés. Seul leur état peut changer.

Un exemple:


Comment changer le texte en bouton en cliquant sur FloatingActionButton.

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

Question:


Comment animer des widgets?

La réponse est:


Utilisation de la classe AnimationController , qui est la descendante de la classe abstraite Animation <T> . En plus de démarrer l'animation, il peut la mettre en pause, revenir en arrière, l'arrêter et la jouer dans la direction opposée. Fonctionne avec Ticker , qui signale un rafraîchissement de l'écran.

Différences:


Sur Android, vous pouvez créer des animations en XML ou animer la vue en utilisant animate (). Dans Flutter, l'animation doit être écrite en code à l'aide de AnimationController.

Information complémentaire:


Vous pouvez en savoir plus dans les widgets Animation & Motion , le didacticiel Animations et la vue d'ensemble Animations .

Un exemple:


Animation en fondu du logo Flutter.

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

Question:


Comment utiliser Canvas ?

La réponse est:


Android et Flutter ont la même API pour Canvas, comme ils utilisent le même moteur Skia de bas niveau.

Différences:


Non.

Information complémentaire:


Flutter a deux classes pour dessiner sur Canvas - CustomPaint et CustomPainter . Le second implémente votre algorithme de rendu.

En savoir plus ici: StackOverflow

Un exemple:


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

Question:


Comment créer des widgets personnalisés?

La réponse est:


Composez des widgets à l'intérieur d'un (au lieu de l'héritage).

Différences:


Dans Android, nous pouvons hériter de la vue qui nous intéresse et ajouter notre propre logique. Dans Flutter, cela ressemble à un ViewGroup, seul le widget est toujours hérité de StatelessWidget ou StatefulWidget. C'est-à-dire vous devez créer un nouveau widget et l'utiliser comme l'ensemble de widgets dont vous avez besoin en tant que paramètres ou champs.

Un exemple:


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

Intentions


Question:


Quel est l'analogue de Intent in Flutter?

La réponse est:


Il n'est pas là. Pour naviguer entre les écrans, les classes Navigator et Route sont utilisées.

Pour interagir avec des composants externes (par exemple, une caméra ou un sélecteur de fichiers), vous pouvez utiliser des plugins ou une intégration native sur chaque plate-forme. En savoir plus sur l'intégration native: Développement de packages et de plugins .

Différences:


Flutter n'a pas d'activité et de fragment. Il existe Navigator (Navigator) et Routes (routes). L'application Flutter ressemble à une application à activité unique, où différents écrans représentent différents fragments, et FragmentManager les contrôle. Navigator est similaire au FragmentManager en principe. Il peut faire push () ou pop () sur l'itinéraire que vous spécifiez. La route est une sorte de fragment, mais dans Flutter, il est habituel de la comparer avec un écran ou une page.

Dans Android, nous décrivons toutes les activités entre lesquelles nous pouvons naviguer dans AndroidManifest.xml.

Flutter a deux façons:

  • Décrire une carte nommée Route (MaterialApp)
  • Accédez directement à Route (WidgetApp).

Un exemple:


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

Question:


Comment traiter les intentions provenant d'autres applications?

La réponse est:


Interagir avec la couche Android de l'application via le MethodChannel .

Un exemple:


Nous écrivons intention-filtre dans 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> 

Nous traitons l'intention dans MainActivity et appelons le code du Flutter via le MethodChannel:

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

Nous demandons des données lorsque le widget commence à être dessiné:

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

Question:


Quel est l'analogue de startActivityForResult () ?

La réponse est:


Le mot-clé wait et le résultat d'une classe Future .

Différences:


Après avoir appelé startActivityForResult () dans Android, nous devons implémenter le traitement dans onActivityResult (). Flutter n'a pas besoin d'implémenter quoi que ce soit, car la méthode du navigateur push () renvoie un objet Future.

Un exemple:


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

Et quand nous avons obtenu les coordonnées sur l'écran '/ location', faites pop ():

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

Async ui


Question:


Quel est l'analogue de runOnUiThread () dans Flutter?

La réponse est:


Dart implémente un modèle d'exécution monothread qui s'exécute sur des isolats . L'exécution asynchrone utilise async / wait, que vous connaissez peut-être avec les coroutines C #, JavaScript ou Kotlin.

Un exemple:


Répondre à la demande et renvoyer le résultat de la mise à jour de l'interface utilisateur:

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

Lorsque la réponse à la demande est reçue, vous devez appeler la méthode setState () pour redessiner l'arborescence du widget avec les nouvelles données.

Un exemple:


Chargement et mise à jour des données dans un 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); }); } } 

Question:


Comment exécuter du code dans un thread d'arrière-plan?

La réponse est:


Comme mentionné ci-dessus - en utilisant async / wait et isolation (Isolate).

Différences:


Hors de la boîte sur Android, vous pouvez utiliser AsyncTask. Vous devez y implémenter onPreExecute () , doInBackground () , onPostExecute () . Dans Flutter «prêt à l'emploi», il vous suffit d'utiliser asynchrone / attendre, Dart s'occupe du reste.

Un exemple:


Ici, la méthode dataLoader () est isolée. Isolément, vous pouvez exécuter des opérations lourdes telles que l'analyse de gros JSON, le chiffrement, le traitement d'image, etc.

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

Question:


Qu'est- ce que l'équivalent Flutter d' OkHttp ?

La réponse est:


Flutter a son propre package HTTP .

Information complémentaire:


Jusqu'à présent, toutes les fonctionnalités OkHttp n'ont pas été implémentées dans le package HTTP, de nombreuses fonctionnalités manquantes sont donc abstraites et vous pouvez les implémenter vous-même si nécessaire.

Un exemple:


Pour utiliser le package HTTP, ajoutez-le en tant que dépendance dans pubspec.yaml:

 dependencies: ... http: ^0.11.3+16 

Pour exécuter la demande, appelez wait dans la fonction asynchrone 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); }); } } 

Question:


Comment montrer les progrès?

La réponse est:


Utilisation du widget ProgressIndicator .

Un exemple:


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

Structure et ressources du projet


Question:


Où stocker les ressources de différentes résolutions?

La réponse est:


En actifs.

Différences:


Dans Android, les ressources ont un dossier res et ont des ressources. Flutter n'a que des atouts. Le dossier des ressources peut être situé n'importe où dans le projet, et surtout, écrivez le chemin d'accès dans le fichier pubspec.yaml.

Information complémentaire:


Comparaison des tailles de ressources graphiques dans Android et Flutter.
Qualificateur de densité AndroidRapport de pixels de flottement
ldpi0,75x
mdpi1.0x
hdpi1,5x
xhdpi2.0x
xxhdpi3.0x
xxxhdpi4,0x
Flutter utilise AssetManager ou des classes spécialisées commençant par Asset pour utiliser les ressources dans le code.

Un exemple:


AssetManager:

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

Emplacement des ressources:

 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 

Chemin dans le fichier pubspec.yaml:

 assets: - images/my_icon.jpeg 

Utilisation de AssetImage :

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

Utilisation directe de l'actif:

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

Question:


Où stocker les cordes? Comment les localiser?

La réponse est:


Conserver dans des champs statiques. Localisez à l'aide du package intl .

Un exemple:


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

Question:


Quel est l'analogue du fichier Gradle? Comment ajouter des dépendances?

La réponse est:


pubspec.yaml.

Information complémentaire:


Flutter délègue l'assemblage aux constructeurs natifs Android et iOS. Voir une liste de toutes les bibliothèques populaires pour Flutter au Pub .

Activités et fragments


Question:


Quel est l'équivalent de l' activité et du fragment dans Flutter?

La réponse est:


Tout dans Flutter est des widgets. Le rôle de l'activité et des fragments pour travailler avec l'interface utilisateur est joué par les widgets. Et le rôle de la navigation, comme mentionné dans le paragraphe sur la navigation, est Navigateur et Route.

Information complémentaire:


Flutter pour les développeurs Android: comment concevoir une interface utilisateur d'activité dans Flutter .

Question:


Comment gérer les événements du cycle de vie?

La réponse est:


Utilisation de la méthode WidgetsBinding et didChangeAppLifecycleState () .

Information complémentaire:


Flutter utilise FlutterActivity dans le code natif et le moteur Flutter rend les changements d'état de traitement aussi discrets que possible. Mais si vous avez encore besoin de travailler selon l'état, le cycle de vie est légèrement différent:

  • inactif - cette méthode est uniquement dans iOS, dans Android il n'y a pas d'analogue;
  • en pause - similaire à onPause () dans Android;
  • reprise - similaire à onPostResume () dans Android;
  • suspension - similaire à onStop dans Android, dans iOS il n'y a pas d'analogue.

Ceci est décrit plus en détail dans la documentation AppLifecycleStatus .

Un exemple:


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

Disposition


Question:


Quel est l'analogue de LinearLayout ?

La réponse est:


Ligne pour horizontal, colonne pour vertical.

Information complémentaire:


Flutter pour les développeurs Android: comment concevoir LinearLayout dans Flutter?

Un exemple:


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

Question:


Quel est l'équivalent de RelativeLayout ?

La réponse est:


Widget Stack .

Plus de détails:

Stackoverflow

Question:


Quel est l'analogue de ScrollView ?

La réponse est:


ListView avec des widgets.

Un exemple:


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

Question:


Comment gérer les transitions entre portrait et paysage?

La réponse est:


FlutterView gère les flips si AndroidManifest.xml contient
android: configChanges = "orientation | screenSize"

Gestes et gestion des événements tactiles


Question:


Comment ajouter un écouteur onClick pour un widget dans Flutter?

La réponse est:


Si le widget prend en charge les clics, alors dans onPressed (). Sinon, alors dans onTap ().

Un exemple:


Dans onPressed ():

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

Dans onTap ():

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

Question:


Comment gérer d'autres gestes sur les widgets?

La réponse est:


Utilisation de GestureDetector . Ils peuvent gérer les actions suivantes:

Appuyez sur



Appuyez deux fois



Appui long



Traînée verticale



Glissement horizontal



Un exemple:


Traitement sur Double Tap:

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

Vues de liste et adaptateurs


Question:


Quel est l'analogue de ListView dans Flutter?

La réponse est:


ListView

Différences:


Flutter n'a pas à penser au nettoyage et à la réutilisation des éléments (c'est ce que fait ListView / RecyclerView dans Android, en utilisant le modèle ViewHolder).

Un exemple:


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

Question:


Comment savoir sur quel élément on a cliqué?

La réponse est:


Envelopper un élément dans un GestureDetector .

Un exemple:


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

Question:


Comment mettre à jour dynamiquement un ListView ?

La réponse est:


Si vous avez un petit ensemble de données, cela peut être fait via setState () . Si l'ensemble de données est volumineux, alors via ListView.Builder , qui est un analogue de RecyclerView .

Un exemple:


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

À l'aide de 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'); }); }, ); } } 

Travailler avec du texte


Question:


Comment utiliser des polices personnalisées?

La réponse est:


Il vous suffit de mettre le fichier de police dans le dossier (pensez au nom vous-même) et d'indiquer le chemin d'accès à celui-ci dans pubspec.yaml.

Un exemple:


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

Question:


Comment styliser des widgets de texte?

La réponse est:


Utilisation de paramètres:

  • la couleur;
  • décoration;
  • decorationColor;
  • decorationStyle;
  • fontFamily;
  • fontSize;
  • fontStyle;
  • fontWeight;
  • hashCode;
  • height;
  • inherit;
  • letterSpacing;
  • textBaseline;
  • wordSpacing.


: Retrieve the value of a text field .

:


hint TextInput ?

La réponse est:


InputDecoration , .

Un exemple:


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

:


?

La réponse est:


InputDecoration .

Un exemple:


 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?

La réponse est:


geolocator .

:


?

La réponse est:


image_picker .

:


Facebook?

La réponse est:


flutter_facebook_login .

:


Firebase?

La réponse est:


Firebase Flutter first party plugins .


:


() ?

La réponse est:


Flutter EventBus . : developing packages and plugins .

:


NDK?

La réponse est:


NDK- Flutter. Flutter .

Themes


:


(Theme) ?

La réponse est:


MaterialApp WidgetApp .

Un exemple:


 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?

La réponse est:


Shared_Preferences plugin ( NSUserDefaults iOS ).

Un exemple:


 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?

La réponse est:


SQFlite .

Notifications


:


push-?

La réponse est:


Firebase_Messaging .

Conclusion


. , , . « » . «-» . , ? 2016 Kotlin, - 2017. , , . , .
2016 Flutter Dart. , 2018 . . ! , , , . ( Google Fuchsia , , , Flutter ). — ! , — . C’est tout pour moi. Google Play!

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


All Articles