[Por los muelles] Aleteo. Parte 5. Para desarrolladores de Xamarin.Forms

Esta es la parte final de una interpretación concisa de la documentación de Flutter que será útil para los desarrolladores de Xamarin.Forms. Dada la situación actual, ¡ahora es el momento de aprender algo nuevo! Debajo del corte, puede encontrar suficiente información para evaluar si vale la pena pasar de un marco multiplataforma a otro y cuánto tiempo llevará.



Si esta información no es suficiente o si tiene experiencia en desarrollo nativo para una plataforma específica, le recomiendo buscar en otras partes:

Flutter. Parte 1. Para desarrolladores de Android
Flutter. Parte 2. Para desarrolladores de iOS
Flutter. Parte 3. Para los desarrolladores de React Native
Flutter. Parte 4. Para desarrolladores web de
Flutter. Parte 5. Para desarrolladores de Xamarin.Forms

Contenido:


  1. Proyecto

  2. Views



  3. Async UI





  4. Layouts



  5. ListView

  6. Texto

  7. Flutter Plugins

  8. Código específico de plataforma

  9. Depuración

  10. Almacenamiento local



Proyecto


Pregunta:


¿Dónde está el punto de entrada?

Responder:


Función main().

Diferencias:


En Xamarin.Forms esto es LoadApplication(new App());.

Ejemplo:


void main() {
  runApp(new MyApp());
}

Pregunta:


¿Cómo crear una página o elemento ?

Responder:


Flutter no tiene concepto de página y elemento como tal. Todos los componentes son widgets. 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. Aquellos. 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 la clase 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.

Ejemplo:


StatelessWidget:
class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

StatefulWidget:
class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        // Take the value from the MyHomePage object that was created by
        // the App.build method, and use it to set the appbar title.
        title: new Text(widget.title),
      ),
      body: new Center(
        // Center is a layout widget. It takes a single child and positions it
        // in the middle of the parent.
        child: new Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            new Text(
              'You have pushed the button this many times:',
            ),
            new Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),
      floatingActionButton: new FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: new Icon(Icons.add),
      ),
    );
  }
}

Puntos de vista


Pregunta:


¿Cómo organizar los widgets? ¿Cuál es el equivalente de un XAMLarchivo?

Responder:


En Flutter, el diseño se realiza directamente en el código utilizando el árbol de widgets.

Diferencia:


En Xamarin.Forms, el diseño más a menudo se realiza en un XAMLarchivo. Flutter no tiene equivalente.

Información Adicional:


La lista de widgets se puede ver aquí .

Ejemplo:


@override
Widget build(BuildContext context) {
  return new Scaffold(
    appBar: new AppBar(
      title: new Text("Sample App"),
    ),
    body: new Center(
      child: new MaterialButton(
        onPressed: () {},
        child: new Text('Hello'),
        padding: new EdgeInsets.only(left: 10.0, right: 10.0),
      ),
    ),
  );
}

Pregunta:


¿Cómo agregar o eliminar un widget a través del código?

Responder:


Actualizando el estado del widget principal y luego reconstruyendo el árbol de widgets.

Diferencias:


En Xamarin.Forms, esto se puede hacer usando la propiedad del Contentelemento o métodos Add()y Remove().

Ejemplo:


class SampleApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Sample App',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new SampleAppPage(),
    );
  }
}

class SampleAppPage extends StatefulWidget {
  SampleAppPage({Key key}) : super(key: key);

  @override
  _SampleAppPageState createState() => new _SampleAppPageState();
}

class _SampleAppPageState extends State<SampleAppPage> {
  // Default value for toggle
  bool toggle = true;
  void _toggle() {
    setState(() {
      toggle = !toggle;
    });
  }

  _getToggleChild() {
    if (toggle) {
      return new Text('Toggle One');
    } else {
      return new CupertinoButton(
        onPressed: () {},
        child: new Text('Toggle Two'),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Sample App"),
      ),
      body: new Center(
        child: _getToggleChild(),
      ),
      floatingActionButton: new FloatingActionButton(
        onPressed: _toggle,
        tooltip: 'Update Text',
        child: new Icon(Icons.update),
      ),
    );
  }
}

Pregunta:


¿Cómo animar un widget?

Responder:


Usando las clases Animation y AnimationController .

Diferencias:


Xamarin.Forms utiliza ViewExtensions y métodos como FadeToo TranslateTo.

Información Adicional:


Lea más sobre animación aquí .

Ejemplo:


import 'package:flutter/material.dart';

void main() {
  runApp(new FadeAppTest());
}

class FadeAppTest extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Fade Demo',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new MyFadeTest(title: 'Fade Demo'),
    );
  }
}

class MyFadeTest extends StatefulWidget {
  MyFadeTest({Key key, this.title}) : super(key: key);
  final String title;
  @override
  _MyFadeTest createState() => new _MyFadeTest();
}

class _MyFadeTest extends State<MyFadeTest> with TickerProviderStateMixin {
  AnimationController controller;
  CurvedAnimation curve;

  @override
  void initState() {
    controller = new AnimationController(duration: const Duration(milliseconds: 2000), vsync: this);
    curve = new CurvedAnimation(parent: controller, curve: Curves.easeIn);
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text(widget.title),
      ),
      body: new Center(
          child: new Container(
              child: new FadeTransition(
                  opacity: curve,
                  child: new FlutterLogo(
                    size: 100.0,
                  )))),
      floatingActionButton: new FloatingActionButton(
        tooltip: 'Fade',
        child: new Icon(Icons.brush),
        onPressed: () {
          controller.forward();
        },
      ),
    );
  }
}

Pregunta:


¿Cómo dibujar en la pantalla?

Responder:


Usando las clases CustomPaint y CustomPainter .

Diferencias:


Xamarin.Forms utiliza un SkiaSharp de terceros . En Flutter, Skia Canvas se usa directamente desde la caja.

Ejemplo:


import 'package:flutter/material.dart';

void main() => runApp(new MaterialApp(home: new DemoApp()));

class DemoApp extends StatelessWidget {
  Widget build(BuildContext context) => new Scaffold(body: new Signature());
}

class Signature extends StatefulWidget {
  SignatureState createState() => new SignatureState();
}

class SignatureState extends State<Signature> {
  List<Offset> _points = <Offset>[];
  Widget build(BuildContext context) {
    return new GestureDetector(
      onPanUpdate: (DragUpdateDetails details) {
        setState(() {
          RenderBox referenceBox = context.findRenderObject();
          Offset localPosition =
          referenceBox.globalToLocal(details.globalPosition);
          _points = new List.from(_points)..add(localPosition);
        });
      },
      onPanEnd: (DragEndDetails details) => _points.add(null),
      child: new CustomPaint(painter: new SignaturePainter(_points), size: Size.infinite),
    );
  }
}

class SignaturePainter extends CustomPainter {
  SignaturePainter(this.points);
  final List<Offset> points;
  void paint(Canvas canvas, Size size) {
    var paint = new 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;
}

Pregunta:


¿Cómo cambiar la transparencia?

Responder:


Usando el widget de opacidad .

Diferencias:


Xamarin.Forms usa la Opacidad de VisualElement . En Flutter, solo necesitas envolver el widget deseado en Opacity .

Pregunta:


¿Cómo crear widgets personalizados?

Responder:


Componga dentro de uno (en lugar de herencia).

Diferencias:


En Xamarin.Forms, puede heredar del VisualElement que nos interesa y agregar su propia lógica. En Flutter, un widget siempre se hereda de StatelessWidget y StatefulWidget . Aquellos. debe crear un nuevo widget y usar el conjunto de widgets necesarios como parámetros o campos.

Ejemplo:


class CustomButton extends StatelessWidget {
  final String label;

  CustomButton(this.label);

  @override
  Widget build(BuildContext context) {
    return new RaisedButton(onPressed: () {}, child: new Text(label));
  }
}

Navegación


Pregunta:


¿Cómo navegar entre pantallas?

Responder:


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

Diferencias:


Xamarin.Forms usa NavigationPage .

Flutter tiene dos métodos de navegación que son similares a NavigationPage :

  1. Mapa de los nombres para describir la ruta ;
  2. Navega directamente a la ruta .

El navegador puede hacer push () o pop () a la ruta que especifique.

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?

Responder:


Uso de implementaciones nativas a través de MethodChannel o url_launcher plugin .

IU asíncrona


Pregunta:


¿Cuál es el equivalente de Device.BeginInvokeOnMainThread () ? ¿Cómo ejecutar el código de forma asincrónica?

Responder:


Dart implementa un modelo de ejecución de subproceso único que se ejecuta en aislamientos . Para la ejecución asincrónica, use async / await, con el que está familiarizado desde C #.

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 de carga y actualización de 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 hacer solicitudes de red?

Responder:


Usando el complemento http .

Ejemplo:


Conecte la dependencia a través de pubspec.yaml:
dependencies:
  ...
  http: ^0.11.3+16

Investigación:
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 de las operaciones largas?

Responder:


Usando ProgressIndicator .

Diferencias:


En Xamarin.Forms, esto se puede hacer directamente colocando un indicador de progreso en XAML.

Ejemplo:


import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

void main() {
  runApp(new SampleApp());
}

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

class SampleAppPage extends StatefulWidget {
  SampleAppPage({Key key}) : super(key: key);

  @override
  _SampleAppPageState createState() => new _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 new Center(child: new CircularProgressIndicator());
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        appBar: new AppBar(
          title: new Text("Sample App"),
        ),
        body: getBody());
  }

  ListView getListView() => new ListView.builder(
      itemCount: widgets.length,
      itemBuilder: (BuildContext context, int position) {
        return getRow(position);
      });

  Widget getRow(int i) {
    return new Padding(padding: new EdgeInsets.all(10.0), child: new 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?

Responder:


En assets.

Diferencias:


Xamarin.Forms no tiene un almacenamiento unificado para los recursos de la plataforma, por lo que debe almacenarlos en carpetas de la plataforma por separado. Hay un aleteo assets. La carpeta assetsse puede ubicar en cualquier parte del proyecto, lo más importante es escribir la ruta en el archivo pubspec.yaml.

Información Adicional:


Comparación de los tamaños de los recursos gráficos en Android y Flutter.
Calificador de densidad de AndroidRelación de píxeles de aleteo
ldpi0.75x
mdpi1.0x
hdpi1.5x
xhdpi2.0x
xxhdpi3.0x
xxxhdpi4.0x
Ejemplo de diseño de recursos:
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

Un ejemplo de una ruta en un pubspec.yamlarchivo:
assets:
 - images/my_icon.jpeg

Un ejemplo de uso de AssetImage :
return AssetImage("images/a_dot_burr.jpeg");

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

Pregunta:


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

Responder:


Flutter no tiene un lugar específico para almacenar cadenas en este momento. Se propone que se almacenen como campos estáticos en una clase separada. Los complementos se utilizan para la localización, por ejemplo flutter_localizations o l10n .

Diferencias:


Xamarin.Forms usa un archivo resx.

Ejemplo:


Almacenamiento:
class Strings {
  static String welcomeMessage = "Welcome To Flutter";
}

Utilizando:
new Text(Strings.welcomeMessage)

Localización:
dependencies:
  # ...
  flutter_localizations:
    sdk: flutter
  intl: "^0.15.6"

import 'package:flutter_localizations/flutter_localizations.dart';

new MaterialApp(
 localizationsDelegates: [
   // Add app-specific localization delegate[s] here.
   GlobalMaterialLocalizations.delegate,
   GlobalWidgetsLocalizations.delegate,
 ],
 supportedLocales: [
    const Locale('en', 'US'), // English
    const Locale('he', 'IL'), // Hebrew
    // ... other locales the app supports
  ],
  // ...
)

Pregunta:


¿Dónde está el archivo del proyecto?

Responder:


Flutter no tiene un archivo de proyecto para abrir este proyecto en el entorno de desarrollo. El archivo similar más cercano pubspec.yaml- contiene dependencias de complementos y detalles del proyecto.

Ciclo de vida de la aplicación


Pregunta:


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

Responder:


Usando el método WidgetsBinding y didChangeAppLifecycleState () .

Información Adicional:


Flutter usa FlutterActivity en el código de Android y FlutterAppDelegate en iOS; debido a esto, el motor Flutter hace que los cambios de estado de procesamiento sean lo más discretos posible. Pero si aún necesita hacer algún trabajo según el estado, el ciclo de vida es ligeramente diferente:
  • inactivo: este método es solo en iOS, en Android no hay análogo;
  • en pausa: similar a onPause () en Android;
  • resumido: similar a onPostResume () en Android;
  • suspensión: similar a onStop en Android, en iOS no hay análogo.

Consulte la documentación de AppLifecycleStatus para obtener más detalles .

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 análogo de StackLayout ?

Responder:


El análogo de StackLayout con orientación vertical es Column , y con Horizontal - Row .

Ejemplo:


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

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

Pregunta:


¿Cuál es el análogo de la cuadrícula ?

Responder:


El GridView .

Ejemplo:


GridView.count(
  // Create a grid with 2 columns. If you change the scrollDirection to
  // horizontal, this would produce 2 rows.
  crossAxisCount: 2,
  // Generate 100 widgets that display their index in the List
  children: List.generate(100, (index) {
    return Center(
      child: Text(
        'Item $index',
        style: Theme.of(context).textTheme.headline,
      ),
    );
  }),
);

Pregunta:


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

Responder:


El análogo más cercano es SingleChildScrollView . Pero Flutter con mayor frecuencia usa ListView para crear contenido que se está rastreando .

Ejemplo:


SingleChildScrollView:
@override
Widget build(BuildContext context) {
  return new SingleChildScrollView(
    child: new Text('Long Content'),
  );
}

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

Manejo de gestos


Pregunta:


¿Cómo manejar los clics?

Responder:


Si el widget admite el método onPressed, puede manejar el clic usándolo. De lo contrario, esto se puede hacer a través del GestureDetector .

Ejemplo:


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

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

Pregunta:


¿Cómo manejar los gestos?

Responder:


Usando el GestureDetector . Pueden manejar las siguientes acciones:

Grifo



Doble toque



Pulsación larga



Arrastre vertical



Arrastre horizontal



Ejemplo:


AnimationController controller;
CurvedAnimation curve;

@override
void initState() {
  controller = new AnimationController(duration: const Duration(milliseconds: 2000), vsync: this);
  curve = new CurvedAnimation(parent: controller, curve: Curves.easeIn);
}

class SampleApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        body: new Center(
          child: new GestureDetector(
            child: new RotationTransition(
                turns: curve,
                child: new FlutterLogo(
                  size: 200.0,
                )),
            onDoubleTap: () {
              if (controller.isCompleted) {
                controller.reverse();
              } else {
                controller.forward();
              }
            },
        ),
    ));
  }
}

ListView y adaptadores


Pregunta:


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

Responder:


El ListView .

Diferencias:


En Xamarin.Forms, debe crear una ViewCell y (con mayor frecuencia) un DataTemplateSelector y pasarlos a ListView . En Flutter, solo necesita pasar una lista de widgets para mostrar.

Ejemplo:


import 'package:flutter/material.dart';

void main() {
  runApp(new SampleApp());
}

class SampleApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Sample App',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new SampleAppPage(),
    );
  }
}

class SampleAppPage extends StatefulWidget {
  SampleAppPage({Key key}) : super(key: key);

  @override
  _SampleAppPageState createState() => new _SampleAppPageState();
}

class _SampleAppPageState extends State<SampleAppPage> {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Sample App"),
      ),
      body: new ListView(children: _getListData()),
    );
  }

  _getListData() {
    List<Widget> widgets = [];
    for (int i = 0; i < 100; i++) {
      widgets.add(new Padding(padding: new EdgeInsets.all(10.0), child: new Text("Row $i")));
    }
    return widgets;
  }
}

Pregunta:


¿Cómo determinar en qué elemento se hizo clic?

Responder:


En Flutter, un widget de elemento debe manejar su clic.

Diferencias:


Xamarin.Forms usa más comúnmente ItemTapped .

Ejemplo:


import 'package:flutter/material.dart';

void main() {
  runApp(new SampleApp());
}

class SampleApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Sample App',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new SampleAppPage(),
    );
  }
}

class SampleAppPage extends StatefulWidget {
  SampleAppPage({Key key}) : super(key: key);

  @override
  _SampleAppPageState createState() => new _SampleAppPageState();
}

class _SampleAppPageState extends State<SampleAppPage> {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Sample App"),
      ),
      body: new ListView(children: _getListData()),
    );
  }

  _getListData() {
    List<Widget> widgets = [];
    for (int i = 0; i < 100; i++) {
      widgets.add(new GestureDetector(
        child: new Padding(
            padding: new EdgeInsets.all(10.0),
            child: new Text("Row $i")),
        onTap: () {
          print('row tapped');
        },
      ));
    }
    return widgets;
  }
}

Pregunta:


¿Cómo actualizar dinámicamente un ListView ?

Responder:


Actualice la lista de datos y llame setState().

Diferencias:


Xamarin.Forms utiliza un ItemsSource para esto . En Flutter, después de que el setState()widget se vuelva a dibujar.

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

Información Adicional:


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

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

Texto


Pregunta:


¿Cómo usar fuentes personalizadas?

Responder:


El archivo de fuente que solo necesita poner en una carpeta (piense en un nombre para usted) e indique la ruta a él pubspec.yaml.

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?

Responder:


Usando parámetros:

  • color;
  • decoración;
  • decoraciónColor;
  • decoraciónEstilo;
  • Familia tipográfica;
  • tamaño de fuente;
  • Estilo de fuente;
  • fontWeight;
  • código hash;
  • altura;
  • heredar;
  • espaciado de letras;
  • textBaseline;
  • wordSpacing.

Pregunta:


¿Cuál es el equivalente de Placeholder ?

Responder:


El hintText propiedad de InputDecoration .

Ejemplo:


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

Pregunta:


¿Cómo mostrar errores de validación?

Responder:


De todos modos con InputDecoration y su estado.

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 Plugins


Pregunta:


¿Cómo acceder al GPS?

Responder:


Usando el complemento geolocalizador .

Pregunta:


¿Cómo acceder a la cámara?

Responder:


Usando el complemento image_picker .

Pregunta:


¿Cómo iniciar sesión a través de Facebook?

Responder:


Usando el complemento flutter_facebook_login .

Pregunta:


¿Cómo usar firebase?

Responder:


Firebase es compatible con los complementos de Flutter :

Código específico de plataforma


Pregunta:


¿Cómo determinar en qué plataforma se está ejecutando el código?

Responder:


Usando la clase de campo platformen Theme o la clase Platform .

Ejemplo:


Campo platform:
if (Theme.of(context).platform == TargetPlatform.iOS) {
  return 'iOS';
} else if (Theme.of(context).platform == TargetPlatform.android) {
  return 'android';
} else if (Theme.of(context).platform == TargetPlatform.fuchsia) {
  return 'fuchsia';
} else {
  return 'not recognised ';
}

Clase de plataforma:
if (Platform.isIOS) {
  return 'iOS';
} else if (Platform.isAndroid) {
  return 'android';
} else if (Platform.isFuchsia) {
  return 'fuchsia';
} else {
  return 'not recognised ';
}

Pregunta:


¿Cómo llamar al código de plataforma nativa?

Responder:


Vía MethodChannel .

Información Adicional:


Más detalles aquí .

Depuración


Pregunta:


¿Cuáles son las herramientas para depurar la aplicación?

Responder:


DevTools .

Pregunta:


¿Cómo hacer hot reload?

Responder:


Si la aplicación se inició desde IntelliJ IDE, Android Studio o VSCode, combinándola ⌘s/ctrl-so haciendo clic en el icono hot reload. Si comenzó desde la terminal, ingrese una letra r.

Pregunta:


¿Cómo acceder al menú de desarrollador en la aplicación?

Responder:


Si el lanzamiento fue desde el IDE, entonces usando las herramientas IDE. Si desde la consola, use h.

Información Adicional:


Lista completa de comandos:

ActuarEquipo en terminalFunciones y campos
Jerarquía de widgetswdebugDumpApp ()
Renderizar árboltdebugDumpRenderTree ()
CapasLdebugDumpLayerTree ()
AccesibilidadS (orden transversal) o U (orden de prueba de impacto inverso)debugDumpSemantics ()
Inspector de widgetsyoWidgetsApp.showWidgetInspectorOverride
Mostrar líneas de construcciónpagsdebugPaintSizeEnabled
Simulación de diferentes sistemas operativosodefaultTargetPlatform
ActuaciónPAGSWidgetsApp. showPerformanceOverlay
Captura de pantalla de flutter.pngs
Aplicación cerrarq

Almacenamiento local


Pregunta:


¿Cómo almacenar key-valuedatos en la aplicación?

Responder:


Usando el complemento shared_preferences .

Diferencias:


En Xamarin se utilizan formularios Xam.Plugins.Settings.

Ejemplo:


Dependencia de conexión:
dependencies:
  flutter:
    sdk: flutter
  shared_preferences: ^0.4.3

Utilizando:
SharedPreferences prefs = await SharedPreferences.getInstance();
_counter = prefs.getInt('counter');
prefs.setInt('counter', ++_counter);
setState(() {
  _counter = _counter;
});

Pregunta:


¿Cómo almacenar datos complejos?

Responder:


Uso de complementos de bases de datos como sqflite o colmena .

Aquí están quizás las respuestas a las preguntas básicas. Esto concluye la serie de interpretaciones. Espero que hayan sido útiles para todos los desarrolladores interesados ​​en este marco. Quizás incluso comenzó a escribir en Flutter y lo reclutó en una comunidad amigable de desarrolladores de Flutter. Y pensaré en nuevos artículos para desarrollar la comunidad y hacer del mundo de las aplicaciones un lugar mejor. ¡Que tus formularios no rompan a Xamarin!

All Articles