[Por los muelles] Aleteo. Parte 2. Para desarrolladores de iOS

Después de un largo descanso, continuaré hablando sobre el popular marco Flutter en un formato de preguntas y respuestas. Puede encontrar el primer artículo para desarrolladores de Android aquí , y hoy habrá material útil para desarrolladores para iOS.

Si tiene poco tiempo para un estudio independiente y profundo de la documentación, pero desea comprender qué Flutter es bueno y cómo usarlo, eche un vistazo debajo del gato.



Aleteo Parte 1. Para desarrolladores de Android
Aleteo Parte 2. Para desarrolladores de iOS
Aleteo Parte 3. Para desarrolladores nativos de React
Aleteo Parte 4. Para desarrolladores web
Aleteo Parte 5. Para desarrolladores de Xamarin.Forms

Contenido:


  1. Vistas

  2. La navegación

  3. Roscado y asincronía

  4. Estructura del proyecto y recursos

  5. ViewControllers

  6. Diseños

  7. Gestos y manejo de eventos táctiles

  8. Estilo de aplicación

  9. Formulario de entrada

  10. Flutter Plugins

  11. Bases de datos y almacenamiento local.

  12. Notificaciones



Vistas


Pregunta:


¿Qué es el análogo UIView en Flutter?

La respuesta es:


Widget

Diferencias:


UIView es en realidad lo que estará en la pantalla. Se llama a SetNeedsDisplay () para mostrar los cambios.

Widget : una descripción de lo que aparecerá en la pantalla. Para el cambio se crea de nuevo.

Informacion adicional:


Flutter incluye la biblioteca de widgets de Cupertino . Contiene widgets que implementan las pautas de diseño de Apple .

Pregunta:


¿Cómo actualizar la visualización de widgets?

La respuesta es:


Usando StatefulWidget y su estado . Flutter tiene 2 tipos de widgets: StatelessWidget y StatefulWidget . Funcionan de la misma manera, la única diferencia está en el estado de representación.

Diferencias:


StatelessWidget tiene un estado inmutable. Adecuado para mostrar texto, logotipo, etc. Es decir Si el elemento en la pantalla no debe cambiar durante todo el tiempo de visualización, entonces le conviene. También se puede usar como contenedor para widgets con estado.

StatefulWidget tiene el estado State, que almacena información sobre el estado actual. Si desea cambiar un elemento en la pantalla al realizar alguna acción (una respuesta vino del servidor, el usuario hizo clic en un botón, etc.), esta es su opción.

Un ejemplo:


1) StatelessWidget - Texto

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

2) StatefulWidget: cuando hace clic en el botón (FloatingActionButton), el texto en el widget de texto cambia de Me gusta Flutter a Flutter es impresionante.

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

Pregunta:


¿Cómo diseñar una pantalla con widgets? ¿Dónde está el guión gráfico ?

La respuesta es:


Flutter no tiene Storyboard . Todo está compuesto en el árbol de widgets directamente en el código.

Un ejemplo:


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

Todos los widgets predeterminados en Flutter se pueden ver en el catálogo de widgets .

Pregunta:


¿Cómo agregar o eliminar un componente en el diseño mientras se ejecuta la aplicación?

La respuesta es:


A través de una función que devolverá el widget deseado según el estado.

Diferencias:


En iOS, puede hacer addSubview () o removeFromSuperview (). En Flutter no es posible porque los widgets no cambian. Solo su condición puede cambiar.

Un ejemplo:


Cambie Texto a Botón haciendo clic en 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), ), ); } } 

Pregunta:


¿Cómo animar widgets?

La respuesta es:


Usando la clase AnimationController , que es el descendiente de la clase abstracta Animation <T> . Además de comenzar la animación, puede pausarla, rebobinarla, detenerla y reproducirla en la dirección opuesta. Funciona con Ticker , que informa sobre un nuevo dibujo de la pantalla.

Diferencias:


En iOS, puede animar una vista usando animar (con Duración: animaciones :). En Flutter, la animación debe escribirse en código usando AnimationController.

Informacion adicional:


Puede obtener más información en los widgets de animación y movimiento , tutorial de animaciones y descripción general de animaciones .

Un ejemplo:


Animación desvanecida del logotipo de 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(); } } 

Pregunta:


¿Cómo usar CoreGraphics ?

La respuesta es:


Flutter usa la API Canvas en el motor de bajo nivel Skia en lugar de CoreGraphics. Android usa una API de Canvas similar.

Informacion adicional:


Flutter tiene dos clases para dibujar en Canvas: CustomPaint y CustomPainter . El segundo implementa su algoritmo de renderizado.

Lea más aquí: StackOverflow

Un ejemplo:


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

Pregunta:


¿Cómo cambiar la transparencia de los widgets?

La respuesta es:


Envuelva en el widget de opacidad .

Diferencias:


En iOS, todas las vistas tienen .opacity o .alpha. En Flutter, esta opción reemplaza el widget de contenedor.

Pregunta:


¿Cómo crear widgets personalizados?

La respuesta es:


Componga widgets dentro de uno (en lugar de herencia).

Diferencias:


En iOS, puede heredar de la vista que nos interesa y agregar su propia lógica. En Flutter, un widget siempre se hereda de StatelessWidget o StatefulWidget. Es decir debe crear un nuevo widget y usarlo como el conjunto de widgets que necesita como parámetros o campos.

Un ejemplo:


 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 navegación


Pregunta:


¿Cómo implementar la navegación entre pantallas en Flutter?

La respuesta es:


Para navegar entre pantallas, se utilizan las clases Navegador y Ruta .

Diferencias:


Flutter no tiene conceptos tales como UIViewController y UINavigationController. Hay Navigator (Navigator) y Rutas (rutas). Navigator es similar al UINavigationController en principio. Puede hacer push () o pop () a la ruta que especifique. Route es una especie de UIViewController, pero en Flutter es habitual compararlo con una pantalla o página.

Flutter tiene dos métodos de navegación:

  • Describa un mapa con nombres de ruta
  • Navega directamente a la ruta.

Un ejemplo:


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

Pregunta:


¿Cómo navegar a una aplicación de terceros?

La respuesta es:


O bien interactuando con la capa iOS de la aplicación a través de MethodChannel , o usando el complemento de iniciador de URL .

Pregunta:


¿Cómo hacer que pop vuelva en iOS ViewController?

La respuesta es:


Llamando a SystemNavigator.pop ().

Informacion adicional:


SystemNavigator.pop () del código Dart llama al siguiente código en iOS:
 UIViewController* viewController = [UIApplication sharedApplication].keyWindow.rootViewController; if ([viewController isKindOfClass:[UINavigationController class]]) { [((UINavigationController*)viewController) popViewControllerAnimated:NO]; } 

Si esto no es lo que necesita, puede hacer su implementación a través de MethodChannel .

Roscado y asincronía


Pregunta:


¿Cómo escribir código asincrónico en Flutter?

La respuesta es:


Dart implementa un modelo de ejecución de subproceso único que se ejecuta en aislamientos . La ejecución asincrónica usa async / await, con la que puede estar familiarizado desde C #, JavaScript o las rutinas Kotlin.

Un ejemplo:


Cumplir la solicitud y devolver el resultado para actualizar la interfaz de usuario:

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

Cuando se recibe la respuesta a la solicitud, debe llamar al método setState () para volver a dibujar el árbol de widgets con los nuevos datos.

Un ejemplo:


Cargando y actualizando datos en 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); }); } } 

Pregunta:


¿Cómo ejecutar código en un hilo de fondo?

La respuesta es:


Como se mencionó anteriormente, utilizando async / await y aislamiento (Isolate).

Diferencias:


Fuera de la caja en iOS, puede usar Operation con una posible redefinición de métodos. En Flutter "fuera de la caja" solo necesita usar async / wait, Dart se encargará del resto.

Un ejemplo:


Aquí el método dataLoader () está aislado. De forma aislada, puede ejecutar operaciones pesadas como analizar JSON grandes, cifrado, procesamiento de imágenes, 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; } } 

Pregunta:


¿Cómo hacer solicitudes de red en Flutter?

La respuesta es:


Flutter tiene su propio paquete HTTP .

Un ejemplo:


Para usar el paquete HTTP, agréguelo como una dependencia en pubspec.yaml:

 dependencies: ... http: ^0.11.3+16 

Para ejecutar la solicitud, llame en espera en la función asincrónica 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); }); } } 

Pregunta:


¿Cómo mostrar el progreso?

La respuesta es:


Usando el widget ProgressIndicator .

Un ejemplo:


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

Estructura del proyecto y recursos


Pregunta:


¿Dónde almacenar recursos de diferentes resoluciones?

La respuesta es:


En activos.

Diferencias:


En iOS, los recursos gráficos tienen Images.xcasset, que se encuentran en la carpeta de activos. Flutter solo tiene activos. La carpeta de recursos se puede ubicar en cualquier parte del proyecto, lo más importante es escribir la ruta en el archivo pubspec.yaml.

Informacion adicional:


Los tamaños de los recursos gráficos en iOS y Flutter son idénticos y siguen el formato basado en la densidad.

Ubicación del recurso:

 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 

Ruta en el archivo pubspec.yaml:

 assets: - images/my_icon.png 

Usando AssetImage :

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

Usando el activo directamente:

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

Pregunta:


¿Dónde almacenar cadenas? ¿Cómo localizarlos?

La respuesta es:


Almacenar en campos estáticos. Localizar usando el paquete intl .

Un ejemplo:


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

Pregunta:


¿Cuál es la contraparte de CocoaPods? ¿Cómo agregar dependencias?

La respuesta es:


pubspec.yaml.

Informacion adicional:


Flutter delega la compilación a constructores nativos de Android e iOS. Vea una lista de todas las bibliotecas populares para Flutter en Pub .

ViewControllers


Pregunta:


¿Cuál es el equivalente de ViewController en Flutter?

La respuesta es:


Todo en Flutter son widgets. El papel de ViewController para trabajar con la interfaz de usuario lo juegan los widgets. Y el papel de la navegación, como se menciona en el párrafo sobre navegación, es Navegador y Ruta.

Pregunta:


¿Cómo manejar los eventos del ciclo de vida?

La respuesta es:


Usando el método WidgetsBinding y didChangeAppLifecycleState () .

Informacion adicional:


Flutter usa FlutterAppDelegate en código nativo, y el motor Flutter hace que el manejo de los cambios de estado sea lo más discreto posible. Pero si aún necesita hacer algún trabajo dependiendo del estado, el ciclo de vida es ligeramente diferente:

  • inactivo: la aplicación está inactiva y no recibe información del usuario. Este estado es solo en iOS, en Android no hay análogo;
  • en pausa: la aplicación es actualmente invisible para el usuario, no responde a la entrada del usuario, pero funciona en segundo plano;
  • reanudado: la aplicación es visible y responde a la entrada del usuario;
  • suspensión - aplicación en proceso de detención. Este estado es solo en Android, en iOS no hay análogo.

Esto se describe con más detalle en la documentación de AppLifecycleStatus .

Un ejemplo:


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

Diseños


Pregunta:


¿Cuál es el equivalente de UITableView y UICollectionView ?

La respuesta es:


ListView

Un ejemplo:


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

Pregunta:


¿Cómo sé en qué elemento de la lista se hizo clic?

La respuesta es:


El widget, que es un elemento de la lista, debe manejar el clic en él.

Diferencias:


En iOS, el método tableView: didSelectRowAtIndexPath: es el responsable de esto. En Flutter, el elemento de la lista debe estar envuelto en un widget que maneja los clics, como el GestureDetector .

Pregunta:


¿Cómo actualizar dinámicamente un ListView ?

La respuesta es:


Actualice la lista de datos y llame a setState ().

Diferencias:


En iOS, debe actualizar los datos y llamar al método reloadData. En Flutter, después de setState (), el widget se volverá a dibujar de nuevo.

Un ejemplo:


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


Informacion adicional:


Para crear una lista, se recomienda usar ListView.Builder .

Un ejemplo:


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


Pregunta:


¿Cuál es el análogo de UIScrollView ?

La respuesta es:


ListView con widgets.

Un ejemplo:


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


Informacion adicional:


Más detalles aquí .

Gestos y manejo de eventos táctiles


Pregunta:


¿Cómo agregar onClick listener para widget en Flutter?

La respuesta es:


Si el widget admite clics, entonces en onPressed (). Si no, entonces en onTap ().

Un ejemplo:


En onPressed ():

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

En onTap ():

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

Pregunta:


¿Cómo manejar otros gestos en widgets?

La respuesta es:


Usando el GestureDetector . Pueden manejar las siguientes acciones:

Toque



Doble toque



Pulsación larga



Arrastre vertical



Arrastre horizontal



Un ejemplo:


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


Estilo de aplicación


Pregunta:


¿Cómo usar el tema (Theme) en la aplicación?

La respuesta es:


Usando el widget MaterialApp o WidgetApp como la raíz en la aplicación.

Un ejemplo:


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


Pregunta:


¿Cómo usar fuentes personalizadas?

La respuesta es:


Solo necesita poner el archivo de fuente en la carpeta (piense en el nombre usted mismo) e indique la ruta en pubspec.yaml.

Un ejemplo:


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

Pregunta:


¿Cómo diseñar widgets de texto?

La respuesta es:


Usando parámetros:

  • color
  • decoracion
  • decoraciónColor;
  • decoraciónEstilo;
  • fontFamily;
  • fontSize;
  • fontStyle;
  • fontWeight;
  • hashCode;
  • altura
  • heredar
  • letterSpacing;
  • textBaseline;
  • wordSpacing.


Formulario de entrada


Pregunta:


¿Cómo obtener el resultado de entrada del usuario?

La respuesta es:


Usando TextEditingController .

Un ejemplo:

 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 respuesta es:


InputDecoration , .

Un ejemplo:


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

:


?

La respuesta es:


InputDecoration .

Un ejemplo:


 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 respuesta es:


geolocator .

:


?

La respuesta es:


image_picker .

:


Facebook?

La respuesta es:


flutter_facebook_login .

:


Firebase?

La respuesta es:


Firebase Flutter first party plugins :


:


() ?

La respuesta es:


Flutter EventBus . : developing packages and plugins .


:


UserDefault?

La respuesta es:


Shared_Preferences plugin ( Shared Preferences Android ).

Un ejemplo:


 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 respuesta es:


SQFlite .


:


push-?

La respuesta es:


Firebase_Messaging .

Conclusión


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

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


All Articles