Flutter - Introducción a los diseños

Dado que el concepto central de Flutter es Todo es widget , Flutter incorpora una funcionalidad de diseño de interfaz de usuario en los propios widgets. Flutter proporciona una gran cantidad de widgets especialmente diseñados como Container, Center, Align , etc., solo con el propósito de diseñar la interfaz de usuario. Los widgets se crean componiendo otros widgets normalmente usan widgets de diseño. Aprendamos el concepto de diseño de Flutter en este capítulo.

Tipo de widgets de diseño

Los widgets de diseño se pueden agrupar en dos categorías distintas según su hijo:

  • Widget que apoya a un solo niño
  • Widget que admite varios niños

Aprendamos ambos tipos de widgets y su funcionalidad en las próximas secciones.

Widgets para un solo niño

En esta categoría, los widgets solo tendrán un widget como hijo y cada widget tendrá una funcionalidad de diseño especial.

Por ejemplo, el widget Center simplemente centra su widget secundario con respecto a su widget principal y el widget Container proporciona una flexibilidad completa para colocarlo secundario en cualquier lugar dentro de él usando diferentes opciones como relleno, decoración, etc.

Los widgets de un solo niño son excelentes opciones para crear widgets de alta calidad con una sola funcionalidad, como botón, etiqueta, etc.

El código para crear un botón simple usando el widget Container es el siguiente:

class MyButton extends StatelessWidget {
   MyButton({Key key}) : super(key: key); 

   @override 
   Widget build(BuildContext context) {
      return Container(
         decoration: const BoxDecoration(
            border: Border(
               top: BorderSide(width: 1.0, color: Color(0xFFFFFFFFFF)),
               left: BorderSide(width: 1.0, color: Color(0xFFFFFFFFFF)),
               right: BorderSide(width: 1.0, color: Color(0xFFFF000000)),
               bottom: BorderSide(width: 1.0, color: Color(0xFFFF000000)),
            ),
         ),
         child: Container(
            padding: const
            EdgeInsets.symmetric(horizontal: 20.0, vertical: 2.0),
            decoration: const BoxDecoration(
               border: Border(
                  top: BorderSide(width: 1.0, color: Color(0xFFFFDFDFDF)),
                  left: BorderSide(width: 1.0, color: Color(0xFFFFDFDFDF)),
                  right: BorderSide(width: 1.0, color: Color(0xFFFF7F7F7F)),
                  bottom: BorderSide(width: 1.0, color: Color(0xFFFF7F7F7F)),
               ),
               color: Colors.grey,
            ),
            child: const Text(
               'OK',textAlign: TextAlign.center, style: TextStyle(color: Colors.black)
            ), 
         ), 
      ); 
   }
}

Aquí, hemos utilizado dos widgets: un widget de contenedor y un widget de texto . El resultado del widget es un botón personalizado como se muestra a continuación:

Veamos algunos de los widgets de diseño de un solo niño más importantes proporcionados por Flutter :

  • Padding- Se utiliza para organizar su widget secundario por el relleno dado. Aquí, la clase EdgeInsets puede proporcionar el relleno .

  • Align- Alinee su widget hijo dentro de sí mismo usando el valor de la propiedad de alineación . El valor de la propiedad de alineación puede ser proporcionado por la clase FractionalOffset . La clase FractionalOffset especifica las compensaciones en términos de una distancia desde la parte superior izquierda.

Algunos de los posibles valores de compensaciones son los siguientes:

  • FractionalOffset (1.0, 0.0) representa la parte superior derecha.

  • FractionalOffset (0.0, 1.0) representa la parte inferior izquierda.

A continuación se muestra un código de muestra sobre compensaciones:

Center(
   child: Container(
      height: 100.0, 
      width: 100.0, 
      color: Colors.yellow, child: Align(
         alignment: FractionalOffset(0.2, 0.6),
         child: Container( height: 40.0, width:
            40.0, color: Colors.red,
         ), 
      ), 
   ), 
)
  • FittedBox - Escala el widget secundario y luego lo coloca de acuerdo con el ajuste especificado.

  • AspectRatio - Intenta ajustar el tamaño del widget secundario a la relación de aspecto especificada

  • ConstrainedBox

  • Baseline

  • FractinallySizedBox

  • IntrinsicHeight

  • IntrinsicWidth

  • LiimitedBox

  • OffStage

  • OverflowBox

  • SizedBox

  • SizedOverflowBox

  • Transform

  • CustomSingleChildLayout

Nuestra aplicación Hello World utiliza widgets de diseño basados ​​en materiales para diseñar la página de inicio. Modifiquemos nuestra aplicación hello world para construir la página de inicio usando widgets de diseño básico como se especifica a continuación

  • Container - Widget contenedor genérico, de un solo hijo, basado en caja con alineación, relleno, borde y margen junto con características de estilo enriquecidas.

  • Center - Widget de contenedor secundario simple, único, que centra su widget secundario.

El código modificado del widget MyHomePage y MyApp es el siguiente:

class MyApp extends StatelessWidget {
   @override
   Widget build(BuildContext context) {
      return MyHomePage(title: "Hello World demo app");
   }
}
class MyHomePage extends StatelessWidget {
   MyHomePage({Key key, this.title}) : super(key: key);
   final String title;
   @override
   Widget build(BuildContext context) {
      return Container(
         decoration: BoxDecoration(color: Colors.white,),
         padding: EdgeInsets.all(25), child: Center(
            child:Text(
               'Hello World', style: TextStyle(
                  color: Colors.black, letterSpacing: 0.5, fontSize: 20,
               ),
               textDirection: TextDirection.ltr,
            ),
         )
      );
   }
}

Aquí,

  • El widget de contenedor es el widget de nivel superior o raíz. El contenedor se configura utilizando la propiedad de decoración y relleno para diseñar su contenido.

  • BoxDecoration tiene muchas propiedades como color, borde, etc., para decorar el widget Container y aquí, el color se usa para establecer el color del contenedor.

  • El relleno del widget Container se establece mediante la clase dgeInsets , que proporciona la opción de especificar el valor de relleno.

  • Center es el widget secundario del widget Container . Nuevamente, Text es el elemento secundario del widget Center . El texto se usa para mostrar el mensaje y el Centro se usa para centrar el mensaje de texto con respecto al widget principal, Container .

El resultado final del código proporcionado anteriormente es una muestra de diseño como se muestra a continuación:

Varios widgets secundarios

En esta categoría, un widget determinado tendrá más de un widget secundario y el diseño de cada widget es único.

Por ejemplo, el widget Fila permite colocar a sus hijos en dirección horizontal, mientras que el widget Columna permite colocar a sus hijos en dirección vertical. Al componer Fila y Columna , se pueden construir widgets con cualquier nivel de complejidad.

Aprendamos algunos de los widgets de uso frecuente en esta sección.

  • Row - Permite disponer sus hijos de forma horizontal.

  • Column - Permite disponer sus hijos de forma vertical.

  • ListView - Permite ordenar sus hijos como lista.

  • GridView - Permite disponer sus hijos como galería.

  • Expanded - Se utiliza para hacer que los hijos del widget Fila y Columna ocupen el máximo de área posible.

  • Table - Widget basado en tablas.

  • Flow - Widget basado en flujo.

  • Stack - Widget basado en pila.

Aplicación de diseño avanzado

En esta sección, aprendamos cómo crear una interfaz de usuario compleja de lista de productos con diseño personalizado utilizando widgets de diseño de hijos únicos y múltiples.

Para ello, siga la secuencia que se indica a continuación:

  • Cree una nueva aplicación Flutter en Android Studio, product_layout_app .

  • Reemplace el código main.dart con el siguiente código:

import 'package:flutter/material.dart'; 
void main() => runApp(MyApp()); 

class MyApp extends StatelessWidget {
   // This widget is the root of your application.
   @override 
   Widget build(BuildContext context) {
      return MaterialApp( 
         title: 'Flutter Demo', theme: ThemeData( 
         primarySwatch: Colors.blue,), 
         home: MyHomePage(title: 'Product layout demo home page'),
      ); 
   } 
} 
class MyHomePage extends StatelessWidget {
   MyHomePage({Key key, this.title}) : super(key: key); 
   final String title; 
      
   @override 
   Widget build(BuildContext context) {
      return Scaffold(
         appBar: AppBar(title: Text(this.title),), 
         body: Center(child: Text( 'Hello World', )), 
      ); 
   }
}
  • Here,

  • Hemos creado el widget MyHomePage extendiendo StatelessWidget en lugar del StatefulWidget predeterminado y luego eliminamos el código relevante.

  • Ahora, cree un nuevo widget, ProductBox de acuerdo con el diseño especificado como se muestra a continuación:

  • El código de ProductBox es el siguiente.

class ProductBox extends StatelessWidget {
   ProductBox({Key key, this.name, this.description, this.price, this.image}) 
      : super(key: key); 
   final String name; 
   final String description; 
   final int price; 
   final String image; 

   Widget build(BuildContext context) {
      return Container(
         padding: EdgeInsets.all(2), height: 120,  child: Card( 
            child: Row(
               mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: <Widget>[
                  Image.asset("assets/appimages/" +image), Expanded(
                     child: Container(
                        padding: EdgeInsets.all(5), child: Column(
                           mainAxisAlignment: MainAxisAlignment.spaceEvenly, 
                              children: <Widget>[ 
                              
                              Text(this.name, style: TextStyle(fontWeight: 
                                 FontWeight.bold)), Text(this.description), 
                              Text("Price: " + this.price.toString()), 
                           ], 
                        )
                     )
                  )
               ]
            )
         )
      );
   }
}
  • Tenga en cuenta lo siguiente en el código:

  • ProductBox ha utilizado cuatro argumentos como se especifica a continuación:

    • name - Nombre del producto

    • descripción - descripción del producto

    • precio - Precio del producto

    • imagen - Imagen del producto

  • ProductBox utiliza siete widgets integrados como se especifica a continuación:

    • Container
    • Expanded
    • Row
    • Column
    • Card
    • Text
    • Image
  • ProductBox está diseñado utilizando el widget mencionado anteriormente. La disposición o jerarquía del widget se especifica en el diagrama que se muestra a continuación:

  • Ahora, coloque una imagen ficticia (ver a continuación) para obtener información del producto en la carpeta de activos de la aplicación y configure la carpeta de activos en el archivo pubspec.yaml como se muestra a continuación:

assets: 
   - assets/appimages/floppy.png 
   - assets/appimages/iphone.png 
   - assets/appimages/laptop.png 
   - assets/appimages/pendrive.png 
   - assets/appimages/pixel.png 
   - assets/appimages/tablet.png

iPhone.png

Pixel.png

Laptop.png

Tablet.png

Pendrive.png

Floppy.png

Finalmente, use el widget ProductBox en el widget MyHomePage como se especifica a continuación:

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

   @override 
   Widget build(BuildContext context) {
      return Scaffold(
         appBar: AppBar(title:Text("Product Listing")), 
         body: ListView(
            shrinkWrap: true, padding: const EdgeInsets.fromLTRB(2.0, 10.0, 2.0, 10.0), 
            children: <Widget> [
               ProductBox(
                  name: "iPhone", 
                  description: "iPhone is the stylist phone ever", 
                  price: 1000, 
                  image: "iphone.png"
               ), 
               ProductBox(
                  name: "Pixel", 
                  description: "Pixel is the most featureful phone ever", 
                  price: 800, 
                  image: "pixel.png"
               ), 
               ProductBox( 
                  name: "Laptop", 
                  description: "Laptop is most productive development tool", 
                  price: 2000, 
                  image: "laptop.png"
               ), 
               ProductBox( 
                  name: "Tablet", 
                  description: "Tablet is the most useful device ever for meeting", 
                  price: 1500, 
                  image: "tablet.png"
               ), 
               ProductBox(
                  name: "Pendrive", 
                  description: "Pendrive is useful storage medium", 
                  price: 100, 
                  image: "pendrive.png"
               ), 
               ProductBox(
                  name: "Floppy Drive", 
                  description: "Floppy drive is useful rescue storage medium", 
                  price: 20, 
                  image: "floppy.png"
               ), 
            ],
         )
      );
   }
}
  • Aquí, hemos utilizado ProductBox como elementos secundarios del widget ListView .

  • El código completo (main.dart) de la aplicación de diseño del producto (product_layout_app) es el siguiente:

import 'package:flutter/material.dart'; 
void main() => runApp(MyApp()); 

class MyApp extends StatelessWidget { 
   // This widget is the root of your application. 
   @override 
   Widget build(BuildContext context) {
      return MaterialApp(
         title: 'Flutter Demo', theme: ThemeData(
            primarySwatch: Colors.blue,
         ), 
         home: MyHomePage(title: 'Product layout demo home page'), 
      );
   }
}
class MyHomePage extends StatelessWidget { 
   MyHomePage({Key key, this.title}) : super(key: key); 
   final String title; 
   
   @override 
   Widget build(BuildContext context) { 
      return Scaffold( 
         appBar: AppBar(title: Text("Product Listing")), 
         body: ListView(
            shrinkWrap: true, 
            padding: const EdgeInsets.fromLTRB(2.0, 10.0, 2.0, 10.0), 
            children: <Widget>[ 
               ProductBox(
                  name: "iPhone", 
                  description: "iPhone is the stylist phone ever", 
                  price: 1000, 
                  image: "iphone.png"
               ), 
               ProductBox( 
                  name: "Pixel",    
                  description: "Pixel is the most featureful phone ever", 
                  price: 800, 
                  image: "pixel.png"
               ), 
               ProductBox( 
                  name: "Laptop", 
                  description: "Laptop is most productive development tool", 
                  price: 2000, 
                  image: "laptop.png"
               ), 
               ProductBox( 
                  name: "Tablet", 
                  description: "Tablet is the most useful device ever for meeting", 
                  price: 1500, 
                  image: "tablet.png"
               ), 
               ProductBox( 
                  name: "Pendrive", 
                  description: "Pendrive is useful storage medium", 
                  price: 100, 
                  image: "pendrive.png"
               ), 
               ProductBox(
                  name: "Floppy Drive", 
                  description: "Floppy drive is useful rescue storage medium", 
                  price: 20, 
                  image: "floppy.png"
               ), 
            ],
         )
      );
   }
}
class ProductBox extends StatelessWidget {
   ProductBox({Key key, this.name, this.description, this.price, this.image}) :
      super(key: key); 
   final String name; 
   final String description; 
   final int price; 
   final String image; 
   
   Widget build(BuildContext context) {
      return Container(
         padding: EdgeInsets.all(2), 
         height: 120, 
         child: Card(
            child: Row(
               mainAxisAlignment: MainAxisAlignment.spaceEvenly, 
               children: <Widget>[ 
                  Image.asset("assets/appimages/" + image), 
                  Expanded( 
                     child: Container( 
                        padding: EdgeInsets.all(5), 
                        child: Column(    
                           mainAxisAlignment: MainAxisAlignment.spaceEvenly, 
                           children: <Widget>[ 
                              Text(
                                 this.name, style: TextStyle(
                                    fontWeight: FontWeight.bold
                                 )
                              ),
                              Text(this.description), Text(
                                 "Price: " + this.price.toString()
                              ), 
                           ], 
                        )
                     )
                  )
               ]
            )
         )
      );
   }
}

El resultado final de la aplicación es el siguiente: