[Par les quais] Flutter. Partie 2. Pour les développeurs iOS

Après une longue pause, je continuerai à parler du cadre Flutter populaire dans un format de questions et réponses. Vous pouvez trouver le premier article pour les développeurs Android ici , et aujourd'hui, il y aura du matériel utile pour les développeurs pour iOS.

Si vous avez peu de temps pour une étude indépendante et approfondie de la documentation, mais que vous voulez comprendre ce que Flutter est bon et comment l'utiliser, jetez un œil sous le chat.



Flutter. Partie 1. Pour les développeurs Android
Flutter. Partie 2. Pour les développeurs iOS
Flutter. Partie 3. Pour les développeurs natifs de React
Flutter. Partie 4. Pour les développeurs Web
Flutter. Partie 5. Pour les développeurs Xamarin.Forms

Contenu:


  1. Vues

  2. La navigation

  3. Filetage et asynchronicité

  4. Structure et ressources du projet

  5. ViewControllers

  6. Disposition

  7. Gestes et gestion des événements tactiles

  8. Style d'application

  9. Formulaire de saisie

  10. Plugins Flutter

  11. Bases de données et stockage local

  12. Notifications



Vues


Question:


Qu'est-ce que l' analogue UIView dans Flutter?

La réponse est:


Widget

Différences:


UIView est en fait ce qui sera affiché à l'écran. SetNeedsDisplay () est appelé pour afficher les modifications.

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

Information complémentaire:


Flutter comprend la bibliothèque Cupertino Widgets . Il contient des widgets qui mettent en œuvre les directives Apple Design .

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 est génial!

 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 storyboard ?

La réponse est:


Flutter n'a pas de Storyboard . 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: CupertinoButton( onPressed: () { setState(() { _pressedCount += 1; }); }, 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 iOS, vous pouvez ajouter addSubview () ou supprimerFromSuperview (). Dans Flutter, ce n'est pas possible car les widgets sont inchangés. Seul leur état peut changer.

Un exemple:


Changez le texte en bouton en cliquant sur FloatingActionButton.

 class SampleApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Sample App', theme: ThemeData( primarySwatch: Colors.blue, ), home: SampleAppPage(), ); } } class SampleAppPage extends StatefulWidget { SampleAppPage({Key key}) : super(key: key); @override _SampleAppPageState createState() => _SampleAppPageState(); } class _SampleAppPageState extends State<SampleAppPage> { // Default value for toggle bool toggle = true; void _toggle() { setState(() { toggle = !toggle; }); } _getToggleChild() { if (toggle) { return Text('Toggle One'); } else { return CupertinoButton( onPressed: () {}, child: Text('Toggle Two'), ); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Sample App"), ), body: Center( child: _getToggleChild(), ), floatingActionButton: FloatingActionButton( onPressed: _toggle, tooltip: 'Update Text', child: Icon(Icons.update), ), ); } } 

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 iOS, vous pouvez animer une vue en utilisant animate (withDuration: animations :). 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.

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

Question:


Comment utiliser CoreGraphics ?

La réponse est:


Flutter utilise l'API Canvas sur le moteur de bas niveau Skia au lieu de CoreGraphics. Android utilise une API Canvas similaire.

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:


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

Question:


Comment changer la transparence des widgets?

La réponse est:


Enveloppez dans le widget Opacity .

Différences:


Sur iOS, toutes les vues ont .opacity ou .alpha. Dans Flutter, cette option remplace le widget wrapper.

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 iOS, vous pouvez hériter de la vue qui nous intéresse et ajouter votre propre logique. Dans Flutter, un 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"), ); } 

La navigation


Question:


Comment implémenter la navigation entre les écrans dans Flutter?

La réponse est:


La navigation entre les écrans utilise les classes Navigator et Route .

Différences:


Flutter n'a pas de concepts tels que UIViewController et UINavigationController. Il existe Navigator (Navigator) et Routes (routes). Navigator est similaire à UINavigationController en principe. Il peut faire push () ou pop () sur l'itinéraire que vous spécifiez. Route est une sorte d'UIViewController, mais dans Flutter, il est habituel de le comparer avec un écran ou une page.

Flutter a deux méthodes de navigation:

  • Décrire une carte avec des noms de route
  • Accédez directement à Route.

Un exemple:


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

Question:


Comment naviguer vers une application tierce?

La réponse est:


Soit en interagissant avec la couche iOS de l'application via MethodChannel , soit en utilisant le plug-in de lancement d'URL .

Question:


Comment faire revenir pop dans iOS ViewController?

La réponse est:


En appelant SystemNavigator.pop ().

Information complémentaire:


SystemNavigator.pop () du code Dart appelle le code suivant dans iOS:
 UIViewController* viewController = [UIApplication sharedApplication].keyWindow.rootViewController; if ([viewController isKindOfClass:[UINavigationController class]]) { [((UINavigationController*)viewController) popViewControllerAnimated:NO]; } 

Si ce n'est pas ce dont vous avez besoin, vous pouvez effectuer votre implémentation via MethodChannel .

Filetage et asynchronicité


Question:


Comment écrire du code asynchrone 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 de 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 iOS, vous pouvez utiliser Operation avec une redéfinition possible des méthodes. 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:


Comment faire des requêtes réseau dans Flutter?

La réponse est:


Flutter a son propre package HTTP .

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 iOS, les ressources graphiques ont Images.xcasset, qui se trouvent dans le dossier des ressources. Flutter n'a que des atouts. Le dossier de 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:


Les tailles des ressources graphiques dans iOS et Flutter sont identiques et suivent le format basé sur la densité.

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

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:


Quelle est la contrepartie de CocoaPods? Comment ajouter des dépendances?

La réponse est:


pubspec.yaml.

Information complémentaire:


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

ViewControllers


Question:


Quel est l'équivalent de ViewController dans Flutter?

La réponse est:


Tout dans Flutter est des widgets. Le rôle de ViewController 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.

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 FlutterAppDelegate dans le code natif et le moteur Flutter rend la gestion des changements d'état aussi discrète que possible. Mais si vous avez encore besoin de travailler selon l'état, le cycle de vie est légèrement différent:

  • inactif - l'application est inactive et ne reçoit aucune entrée d'utilisateur. Cet état est uniquement sous iOS, dans Android il n'y a pas d'analogue;
  • en pause - l'application est actuellement invisible pour l'utilisateur, ne répond pas aux entrées de l'utilisateur, mais fonctionne en arrière-plan;
  • reprise - l'application est visible et répond aux entrées de l'utilisateur;
  • suspension - application en cours d'arrêt. Cet état est uniquement sous 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'équivalent de UITableView et UICollectionView ?

La réponse est:


ListView

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 quel élément de la liste a été cliqué?

La réponse est:


Le widget, qui est un élément de la liste, doit gérer le clic dessus.

Différences:


Dans iOS, la méthode séparée tableView: didSelectRowAtIndexPath: en est responsable. Dans Flutter, l'élément de liste doit être encapsulé dans un widget qui gère les clics, tel que GestureDetector .

Question:


Comment mettre à jour dynamiquement un ListView ?

La réponse est:


Actualisez la liste de données et appelez setState ().

Différences:


Sur iOS, vous devez mettre à jour les données et appeler la méthode reloadData. Dans Flutter, après setState (), le widget sera à nouveau redessiné.

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


Information complémentaire:


Pour créer une liste, il est recommandé d'utiliser ListView.Builder .

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


Question:


Quel est l'analogue d' UIScrollView ?

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


Information complémentaire:


Plus de détails ici .

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


Style d'application


Question:


Comment utiliser le thème (Thème) dans l'application?

La réponse est:


Utiliser le widget MaterialApp ou WidgetApp comme racine dans l'application.

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


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;
  • décorationCouleur;
  • decorationStyle;
  • fontFamily;
  • fontSize;
  • fontStyle;
  • fontWeight;
  • hashCode;
  • hauteur;
  • hériter;
  • letterSpacing;
  • textBaseline;
  • wordSpacing.


Formulaire de saisie


Question:


Comment obtenir le résultat de la saisie utilisateur?

La réponse est:


Utilisation de TextEditingController .

Un exemple:

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


: Retrieve the value of a text field .

:


hint TextInput ?

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:


 class SampleApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Sample App', theme: ThemeData( primarySwatch: Colors.blue, ), home: SampleAppPage(), ); } } class SampleAppPage extends StatefulWidget { SampleAppPage({Key key}) : super(key: key); @override _SampleAppPageState createState() => _SampleAppPageState(); } class _SampleAppPageState extends State<SampleAppPage> { String _errorText; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Sample App"), ), body: Center( child: TextField( onSubmitted: (String text) { setState(() { if (!isEmail(text)) { _errorText = 'Error: This is not an email'; } else { _errorText = null; } }); }, decoration: InputDecoration(hintText: "This is a hint", errorText: _getErrorText()), ), ), ); } _getErrorText() { return _errorText; } bool isEmail(String emailString) { String emailRegexp = r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$'; RegExp regExp = RegExp(emailRegexp); return regExp.hasMatch(emailString); } } 

Flutter


:


GPS?

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 .


:


UserDefault?

La réponse est:


Shared_Preferences plugin ( Shared Preferences Android ).

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

:


Core Data ?

La réponse est:


SQFlite .

Notifications


:


push-?

La réponse est:


Firebase_Messaging .


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

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


All Articles