Pasar de diseño a Flutter – con Adobe XD

Buscando opciones de como pasar del diseño al código ( ver como pasar del diseño en Sketch o Adobe XD a android, react, ios o flutter) , encontré otra opción de hacerlo desde Adobe XD, a diferencia de SuperNova, se puede pasar por partes y no es necesario hacer ajustes al código, al menos para que se vea igual al diseño.

Instalar plugin Adobe XD a Flutter

En Adobe XD menu>complementos>Administrar Complementos

Click en la pestaña “Explorar” y escribir Flutter, instalar “XD to Flutter” > Instalar

Indicara que lo instalo, luego crear o abrir algún diseño de adobe XD

Crear y configurar el proyecto Flutter

flutter create --org=com.example xd2flutter
All done!
In order to run your application, type:

  $ cd xd2flutter
  $ flutter run

To enable null safety, type:

  $ cd xd2flutter
  $ dart migrate --apply-changes

Your application code is in xd2flutter/lib/main.dart.

Agregamos las librerías necesarias que usa el plugin al generar el código
Adobe XD ( https://pub.dev/packages/adobe_xd/install ) y
Flutter SVG ( https://pub.dev/packages/flutter_svg )

// in pubspec.yaml
dependencies:
  adobe_xd: ^1.0.0
  #flutter_svg: ^0.17.1
  flutter_svg: ^0.20.0-nullsafety.3

Si se usa el Flutter SVG que recomienda el tutorial de adobe, obtendrá un error como este

/Library/Flutter/flutter/packages/flutter/lib/src/widgets/localizations.dart:413:17: Context: Found this candidate, but the arguments don't match.
  static Locale localeOf(BuildContext context) {

Para eso se importa su version con el null safety : flutter_svg: ^0.20.0-nullsafety.3 ( https://stackoverflow.com/questions/66098977/flutter-dependency-error-flutter-svg-0-19-21 )

Probamos y obtenemos este error

E/AndroidRuntime(10045): FATAL EXCEPTION: main
E/AndroidRuntime(10045): Process: com.example.xd2flutter, PID: 10045
E/AndroidRuntime(10045): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.xd2flutter/com.example.xd2flutter.MainActivity}: android.content.res.Resources$NotFoundException: File res/drawable-v21/launch_background.xml from drawable resource ID #0x7f040000

El cual indica que no encuentra el recurso launch_background, para resolverlo, se puede comentar el meta en android manifest

 <meta-data
              android:name="io.flutter.embedding.android.SplashScreenDrawable"
              android:resource="@drawable/launch_background"
              />

O en el archivo launch_background.xml, editando el ?android/colorbackground por @android:color/white

<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@android:color/white" />

    <item>
        <bitmap
            android:gravity="center"
            android:src="@mipmap/ic_launcher" />
    </item>
</layer-list>

y corre ok

.

Obtener por widget de Adobe XD para Flutter

En Adobe XD, abrir o crear un diseño, luego ir a menu>complementos> XD to Flutter

Y aparecera un panel izquierdo, el cual mostrará un previo del elemento seleccionado, luego click en “Copy Selected”

Si se ve en blanco, agrupar los elementos

y ya aparece en el previo

Copiando el widget, modifique el main para renderizarlo desde un metodo _getAdobeWidget1

 @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(toolbarHeight: 0),
      body: _getAdobeWidget1(),
    );
  }

  Widget _getAdobeWidget1() {
    return SizedBox(

Tomar en cuenta agrupar los texto con el rectangulo del boton para que haga un widget y no dos superpuestos por stack

Básicamente usa Stack y Pinned, por lo cual agrupar es importante cuando se va ajustar
Tiene tamaños fijos, se podría quitar el widget dentro del Pinned y jugar con Columns y Rows para hacerlo adaptables

Widget _getAdobeWidget1() {
    return SizedBox(
      width: 381.0,
      height: 312.0,
      child: Stack(
        children: <Widget>[
          Pinned.fromSize(
            bounds: Rect.fromLTWH(0.0, 0.0, 381.0, 312.0),
            size: Size(381.0, 312.0),
            pinLeft: true,
            pinRight: true,
            pinTop: true,
            pinBottom: true,
            child: Container(
              decoration: BoxDecoration(
                borderRadius: BorderRadius.circular(35.0),
                color: const Color(0xffffffff),
                boxShadow: [
                  BoxShadow(
                    color: const Color(0x14000000),

Obtener un Widgets mas complejo

Probando un elemento un poco mas complejo

Las imágenes las pasa como SVG y los pinta con la librería Flutter SVG

// Adobe XD layer: 'Item' (group)
      SizedBox(
    width: 124.0,
    height: 113.0,
    child: Stack(
      children: <Widget>[
        Pinned.fromSize(
          bounds: Rect.fromLTWH(0.0, 0.0, 124.4, 112.7),
          size: Size(124.4, 112.7),
          pinLeft: true,
          pinRight: true,
          pinTop: true,
          pinBottom: true,
          child: Container(
            decoration: BoxDecoration(
              borderRadius: BorderRadius.circular(5.0),
              color: const Color(0xffffffff),
              border: Border.all(width: 2.0, color: const Color(0xfff9c229)),
              boxShadow: [
                BoxShadow(
                  color: const Color(0x59bbbbbb),
                  offset: Offset(0, 7),
                  blurRadius: 11,
                ),
              ],
            ),
          ),
        ),
        Pinned.fromSize(
          bounds: Rect.fromLTWH(9.9, 82.6, 104.0, 20.0),
          size: Size(124.4, 112.7),
          pinLeft: true,
          pinRight: true,
          pinBottom: true,
          fixedHeight: true,
          child: Text(
            'Movimientos',
            style: TextStyle(
              fontFamily: 'Rubik',
              fontSize: 17,
              color: const Color(0xff686868),
              fontWeight: FontWeight.w500,
            ),
            textAlign: TextAlign.center,
          ),
        ),
        Pinned.fromSize(
          bounds: Rect.fromLTWH(30.0, 13.2, 65.1, 65.1),
          size: Size(124.4, 112.7),
          pinLeft: true,
          pinRight: true,
          pinTop: true,
          fixedHeight: true,
          child: Container(
            decoration: BoxDecoration(
              borderRadius: BorderRadius.all(Radius.elliptical(9999.0, 9999.0)),
              color: const Color(0x5cffee9b),
            ),
          ),
        ),
        Pinned.fromSize(
          bounds: Rect.fromLTWH(42.0, 24.4, 41.6, 43.1),
          size: Size(124.4, 112.7),
          child:
              // Adobe XD layer: 'icon' (group)
              Stack(
            children: <Widget>[
              SvgPicture.string(
                '<svg viewBox="11.8 90.8 41.6 43.1" ><path  d="M 25.75422286987305 108.2598648071289 C 25.85722923278809 108.0872268676758 25.91364479064941 107.9320526123047 25.91364479064941 107.8038787841797 C 25.91364479064941 94.99887084960938 32.45513534545898 92.0467529296875 34.02599716186523 91.07327270507812 C 34.29887771606445 90.91349792480469 34.28226852416992 90.84697723388672 33.96943664550781 90.84697723388672 C 23.84692001342773 90.84697723388672 17.59330558776855 98.11896514892578 17.59330558776855 107.8038787841797 C 17.59330558776855 107.9621658325195 17.59762001037598 108.1138763427734 17.60567855834961 108.2598648071289 L 11.76902770996094 108.2598648071289 L 21.75347328186035 121.0432281494141 L 31.7379207611084 108.2598648071289 L 25.75422286987305 108.2598648071289 Z" fill="#f9c229" stroke="none" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path transform="translate(-251.76, -170.23)" d="M 305.1342163085938 286.807373046875 L 295.1497802734375 274.0239868164062 L 285.1653442382812 286.807373046875 L 291.1480407714844 286.807373046875 C 291.0456848144531 286.9793701171875 290.9896240234375 287.1339111328125 290.9896240234375 287.2616577148438 C 290.9896240234375 300.068359375 284.4481201171875 303.018798828125 282.8772583007812 303.9922485351562 C 282.6043701171875 304.1536560058594 282.6209716796875 304.2202453613281 282.933837890625 304.2202453613281 C 293.0563354492188 304.2202453613281 299.3099365234375 296.9465637207031 299.3099365234375 287.26171875 C 299.3099365234375 287.10400390625 299.3056335449219 286.952880859375 299.2976379394531 286.8074340820312 L 305.1342163085938 286.8074340820312 Z" fill="#f9c229" stroke="none" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /></svg>',
                allowDrawingOutsideViewBox: true,
              ),
            ],
          ),
        ),
      ],
    ),
  );

Repositorio:
https://github.com/JavierSolis/AdobeXD_to_Flutter_by_widget

Exportar todo el diseño AdobeXD a Flutter

Si no se selecciona un elemento, se exporta todo como widgets, se debe escoger un proyecto creado previamente, se puede indicar la ruta donde se guardará los widgets generados (por defecto lib), la ruta donde se guardara las imágenes (por defecto assets/images)

Luego la ruta del proyecto

Escoger y seleccionar “Continue”, después de un momento se generará en el proyecto los widgets

No olvidar agregar la ruta de assets en pubspec.yaml

  assets:
    - assets/images/

Por ejemplo para ver de adobeXD analitycs 8

Trabajar con imágenes para generar el código de AdobeXD a Flutter

Como mencione, se agrega el asset/images/ en la ruta del pubspec y es la misma ruta que se configura cuando se exporta en Adobe XD

Selecciona una imagen y asignar un nombre en el campo “image export name” y “image parameter”, en caso se quiera recibir como parámetro en Flutter y click en “export image”

Y la imagen se exportar en la carpeta indicada assets/images

Modificando el main.dart, para cargar el widget de la imagen, se muestra pero debe trabajarse para centrarlo

Y así se debe trabajar cada imagen, en caso de no hacerlo, cuando se muestre este widget, mostrará un error por no encontrar la imagen.

Repositorio:
https://github.com/JavierSolis/AdobeXD_to_Flutter_complete

Actualizar diseño y actualizar widget generado

Si se actualiza un widget, solo no se deja seleccionado ningún elemento, y click en “export all widgets”, los widgtes, se generarán nuevamente, igual con las imágenes, OJO; se borrarán todos los cambios que halla realizado, para esto si lo modificó, en mi caso los copio en otro widget.

Añadiendo interacción en Adobe XD y transformarlo en navegación en Flutter

En AdobeXd, en la parte superior seleccionar “Prototipo” y presionar sobre la flecha azul de la foto y jalarlo hasta Analitycs-9 por ejemplo.

Luego seleccionar al vacío y en el panel del plugin , marcar “Prototype interactions” y click en “Export al images” y “Export all widgets”

Una vez exportado, se exporta o aplica hot reload y presionando sobre la foto, pasa a la pantalla Analitycs-9

Fin u.u

Listo, lo demás es experimentar, que tanto convendría, ya que los tamaños son fijos y es stack+Pinned, que se deben adaptar

Igual que en Supernova, dependerá que tan complejo es el widget, talves en los trabajosos, con sombras bordes y formas, sea conveniente, pero en los mas simples como títulos, sea mas rápido hacerlo directamente.

Referencias:

Blog de Adobe XD donde anuncian el plugin para generar código para Flutter
https://blog.adobe.com/en/2019/12/11/xd-flutter-plugin-generate-dart-code-design-elements.html#gs.xbnowk
https://blog.adobe.com/en/2020/09/10/xd-flutter-plugin-now-available.html#gs.xcpvma

Repositorio del plugin de Adobe XD
https://github.com/AdobeXD/xd-to-flutter-plugin

Solución al error con Flutter SVG
https://stackoverflow.com/questions/66098977/flutter-dependency-error-flutter-svg-0-19-21

Post de como usar Flutter SVG
https://dev.to/mightytechno/render-svg-in-flutter-app-4kan

Repositorio de flutter generado por widget
https://github.com/JavierSolis/AdobeXD_to_Flutter_by_widget

Repositorio de flutter generado de todo el diseño
https://github.com/JavierSolis/AdobeXD_to_Flutter_complete