latest

Introducción a la programación basada en widgets. Barra de Navegación Inferior

Vamos a partir de la aplicación de ejemplo de la lección anterior y vamos a modificarla ligeramente para cambiar el patrón de navegación basado en pestañas, ubicado en la barra superior de aplicaciones (AppBar), por una barra de navegación inferior.

La interfaz de usuario quedará tal y como se muestra  a continuación:

Los cambios necesarios son mínimos y sólo afectan al código fuente en lib/main.dart. Os adjuntamos el código de dicho archivo y posteriormente comentaremos las modificaciones realizadas.

El contenido de lib/main.dart (prestad atención a las importaciones de paquetes y modificarlas con el nombre de vuestro proyecto):

import 'package:flutter/material.dart';
// sustituir "barra_nav_inferior" por el nombre de vuestro proyecto
import 'package:barra_nav_inferior/pestanyas/votodirecto.dart'; 
// sustituir "barra_nav_inferior" por el nombre de vuestro proyecto
import 'package:barra_nav_inferior/pestanyas/opinionpersonal.dart';

void main() {
  runApp(
    new MaterialApp(
      home: new GestorTabs(),
    ),
  );
}

class GestorTabs extends StatefulWidget {
  @override
  _GestorTabsState createState() => _GestorTabsState();
}

class _GestorTabsState extends State<GestorTabs>
    with SingleTickerProviderStateMixin {
  TabController controlador;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    controlador = new TabController(
      length: 2,
      vsync: this,
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: new AppBar(
        title: new Text("¿Qué os parece el curso?"),
        backgroundColor: Colors.orangeAccent,
      ),
      bottomNavigationBar: Container(
        child: new TabBar(
          tabs: <Widget>[
            new Tab(
              icon: Icon(Icons.thumbs_up_down),
            ),
            new Tab(
              icon: Icon(Icons.person),
            ),
          ],
          controller: controlador,
        ),
        color: Colors.orangeAccent,
      ),
      body: new TabBarView(
        controller: controlador,
        children: <Widget>[
          new VotoDirecto(),
          new AreaOpinionPersonal()
        ],
      ),
    );
  }
}

Si nos fijamos con detenimiento en el código anterior, observamos que ya no hacemos uso de la propiedad bottom del widget AppBar. El TabBar queda envuelto por un widget Container al que se ha cambiado el color del fondo. Dicho Container, "cuelga" de la propiedad bottomNavigationBar del Scaffold. La definición del TabBar no varía respecto de la del ejemplo anterior; tampoco lo hace el TabBarView en el cuerpo (propiedad body) del Scaffold.

La definición de los widgets contenidos por el TabBarView no varía respecto de la lección anterior, no obstante, os volvemos a facilitar el código.

El contenido de lib/pestanyas/votodirecto.dart:

import 'package:flutter/material.dart';

class VotoDirecto extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return new Container(
      child:new Center(
        child: new Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: <Widget>[
            new MiCardStateful(
              titulo: "Me gusta",
              color: Colors.greenAccent,
              datosIcono: Icons.thumb_up,
            ),
            new MiCardStateful(
              titulo: "No me gusta",
              color: Colors.redAccent,
              datosIcono: Icons.thumb_down),
          ],
        ),
      ),
    );
  }
}

class MiCardStateful extends StatefulWidget {
  // Propiedades estáticas
  final String titulo;
  final IconData datosIcono;
  final num tamanyoIcono;
  final Color color;

  // Constructor
  MiCardStateful(
      {this.titulo, this.datosIcono, this.tamanyoIcono = 40.0, this.color});

  @override
  _MiCardStatefulState createState() => _MiCardStatefulState();
}

class _MiCardStatefulState extends State<MiCardStateful> 
 with AutomaticKeepAliveClientMixin<MiCardStateful> {
  //Propiedades dinámicas:
  num _contador = 0;

  void _incrementContador() {
    setState(() {
      _contador++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.only(top: 6.0),
      child: new Card(
          child: Container(
              padding: const EdgeInsets.all(8.0),
              child: Column(
                children: <Widget>[
                  Icon(widget.datosIcono,
                      color: widget.color, size: widget.tamanyoIcono),
                  Padding(
                    // padding entre boton e icono
                    padding: EdgeInsets.all(6.0),
                  ),
                  RaisedButton(
                    child: Text(
                      widget.titulo,
                      style: TextStyle(
                        fontSize: 18.0,
                      ),
                    ),
                    onPressed: _incrementContador,
                    color: widget.color,
                    padding: EdgeInsets.all(16.0), // padding interno del botón
                  ),
                  Padding(
                    padding: EdgeInsets.all(6.0),
                  ),
                  Text(_contador.toString(),
                      style: TextStyle(
                        fontSize: 18.0,
                      )), // Otra manera: Text('$_contador')
                ],
              ))),
    );
  }
  @override
  // TODO: implement wantKeepAlive
  bool get wantKeepAlive => true;
}

El contenido de lib/pestanyas/opinionpersonal.dart:

import "package:flutter/material.dart";

enum Acciones { si, no }

class AreaOpinionPersonal extends StatefulWidget {
  @override
  _AreaOpinionPersonalState createState() => _AreaOpinionPersonalState();
}

class _AreaOpinionPersonalState extends State<AreaOpinionPersonal> {
  String _opinion = ""; // Propiedad dinámica
  String _ultOpinionEnviada = ""; // Propiedad dinámica

  TextEditingController opinionController = new TextEditingController();

  void _realizarAccion(Acciones accion) {
    if (accion == Acciones.si) {
      print("Tu opinión personal ha sido enviada");
      setState(() {
        opinionController.clear();
        // Sería Lo mismo que:
        // opinionController.text="";
        _ultOpinionEnviada = _opinion;
        _opinion = "";
      });
    } else {
      print("No se ha mandado tu opinión");
    }
  }

  void _mostrarDialogo() {
    AlertDialog dialogo = new AlertDialog(
      content: new Text('¿Seguro que deseas expresar tu opinión?\n\n'
          '\"$_opinion\"'),
      actions: <Widget>[
        new FlatButton(
            onPressed: () {
              _realizarAccion(Acciones.si);
              // Para cerrar el cuadro de dialogo
              Navigator.pop(context);
            },
            child: new Text("Sí")),
        new FlatButton(
            onPressed: () {
              // Para cerrar el cuadro de dialogo
              _realizarAccion(Acciones.no);
              Navigator.pop(context);
            },
            child: new Text("No"))
      ],
    );
    showDialog(context: context, child: dialogo);
  }

  @override
  Widget build(BuildContext context) {
    return Container(
        padding: const EdgeInsets.all(8.0),
        child: new Center(
            child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: <Widget>[
            TextField(
              decoration: InputDecoration(
                hintText: "Introduce tu opinión personal o lo que quieras...",
              ),
              onChanged: (String data) {
                _opinion = data;
              },
              maxLines: 2,
              controller: opinionController,
            ),
            Padding(
              padding: const EdgeInsets.all(5.0),
            ),
            RaisedButton(
                child: new Text("Enviar opinión personal"),
                onPressed: () {
                  _mostrarDialogo();
                }),
            Text("Última opinión enviada: $_ultOpinionEnviada"),
          ],
        )));
  }
}

Con esto damos por concluida la sección "Programación basada en Widgets. Introducción". En las lecciones siguientes entraremos en profundidad en la utilización de widgets más complejos.

Author image
Iván González is postdoctoral researcher at the Castilla-La Mancha University.
Ciudad Real (Spain)