Flutter usa la conocida AppBar para crear una barra de herramientas, pero cuando necesitamos una barra de herramientas dinámica que muestre contenido al deslizar, usamos el excelente widget SliverAppBar .
Ambos widgets te permiten hacer la aplicación un poco más bella, lo que en Flutter, sin duda, es muy simple.
Vi muchas preguntas en StackOverflow y en grupos de Facebook sobre cómo puede cambiar AppBar y SliverAppBar en términos de comportamiento o diseño.
Veamos dos tareas.
Tarea 1
Queremos crear una barra de aplicaciones que no esté atornillada en la parte superior de la pantalla, pero no como solemos hacer. Queremos agregar un cajón (menú lateral), en cuya apertura responderá la barra de aplicaciones . Eso es todo: nuestra propia barra de aplicaciones con las dimensiones que necesitamos
El problema es que, como sabemos, AppBar tiene un tamaño predeterminado y no podemos cambiarlo. Después de mirar el código fuente, vemos el parámetro appBar en Scaffold , vemos que acepta un widget como PreferredSizeWidget , ahora miramos el código fuente de AppBar y descubrimos que esto es solo StatefulWidget que implementa PreferredSizeWidget .
El punto es pequeño: solo cree nuestro propio widget que implemente PreferredSizeWidget .
Eso es lo que queremos
¿Cómo lo haría para que cuando haga clic en el botón de menú de nuestra barra de aplicaciones, se abra un menú lateral.
Podemos hacer esto de dos maneras:
Usando el `AppBar`
Así es como AppBar puede controlar la apertura de la barra lateral dentro de Scaffold .
class Sample1 extends StatelessWidget { @override Widget build(BuildContext context) { return SafeArea( child: Scaffold( drawer: Drawer(), appBar: MyCustomAppBar( height: 150, ), body: Center( child: FlutterLogo( size: MediaQuery.of(context).size.width / 2, ), ), ), ); } } class MyCustomAppBar extends StatelessWidget implements PreferredSizeWidget { final double height; const MyCustomAppBar({ Key key, @required this.height, }) : super(key: key); @override Widget build(BuildContext context) { return Column( children: [ Container( color: Colors.grey[300], child: Padding( padding: EdgeInsets.all(30), child: AppBar( title: Container( color: Colors.white, child: TextField( decoration: InputDecoration( hintText: "Search", contentPadding: EdgeInsets.all(10), ), ), ), actions: [ IconButton( icon: Icon(Icons.verified_user), onPressed: () => null, ), ], ) , ), ), ], ); } @override Size get preferredSize => Size.fromHeight(height); }
Usando un widget personalizado
Aquí tenemos más flexibilidad y puede usar GlobalKey como ScaffoldState o InheritedWidget de Scaffold , obteniendo así acceso a los métodos de estado para abrir Drawer .
import 'package:flutter/material.dart'; class Sample1 extends StatelessWidget { @override Widget build(BuildContext context) { return SafeArea( child: Scaffold( drawer: Drawer(), appBar: MyCustomAppBar( height: 150, ), body: Center( child: FlutterLogo( size: MediaQuery.of(context).size.width / 2, ), ), ), ); } } class MyCustomAppBar extends StatelessWidget implements PreferredSizeWidget { final double height; const MyCustomAppBar({ Key key, @required this.height, }) : super(key: key); @override Widget build(BuildContext context) { return Column( children: [ Container( color: Colors.grey[300], child: Padding( padding: EdgeInsets.all(30), child: Container( color: Colors.red, padding: EdgeInsets.all(5), child: Row(children: [ IconButton( icon: Icon(Icons.menu), onPressed: () { Scaffold.of(context).openDrawer(); }, ), Expanded( child: Container( color: Colors.white, child: TextField( decoration: InputDecoration( hintText: "Search", contentPadding: EdgeInsets.all(10), ), ), ), ), IconButton( icon: Icon(Icons.verified_user), onPressed: () => null, ), ]), ), ), ), ], ); } @override Size get preferredSize => Size.fromHeight(height); }
Resultado
Simple, ¿verdad? Veamos la segunda tarea para SliverAppBar .
Tarea 2
Como sabemos, SliverAppBar funciona de la siguiente manera:
Lo que queremos es poner la tarjeta integrada en nuestro SliverAppBar , como se muestra en la siguiente figura.
Espere, pero el contenido dentro de SliverAppBar está recortado, por lo que no puede ir más allá de lo que debe hacer.
Sin pánico, veamos el código fuente de SliverAppBar y, oh sorpresa, este es StatefulWidget usando SliverPersistentHeader adentro, ese es todo el secreto.
Crearemos nuestro propio SliverPersistentHeaderDelegate para usar SliverPersistentHeader .
class Sample2 extends StatelessWidget { @override Widget build(BuildContext context) { return SafeArea( child: Material( child: CustomScrollView( slivers: [ SliverPersistentHeader( delegate: MySliverAppBar(expandedHeight: 200), pinned: true, ), SliverList( delegate: SliverChildBuilderDelegate( (_, index) => ListTile( title: Text("Index: $index"), ), ), ) ], ), ), ); } } class MySliverAppBar extends SliverPersistentHeaderDelegate { final double expandedHeight; MySliverAppBar({@required this.expandedHeight}); @override Widget build( BuildContext context, double shrinkOffset, bool overlapsContent) { return Stack( fit: StackFit.expand, overflow: Overflow.visible, children: [ Image.network( "https://images.pexels.com/photos/396547/pexels-photo-396547.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500", fit: BoxFit.cover, ), Center( child: Opacity( opacity: shrinkOffset / expandedHeight, child: Text( "MySliverAppBar", style: TextStyle( color: Colors.white, fontWeight: FontWeight.w700, fontSize: 23, ), ), ), ), Positioned( top: expandedHeight / 2 - shrinkOffset, left: MediaQuery.of(context).size.width / 4, child: Opacity( opacity: (1 - shrinkOffset / expandedHeight), child: Card( elevation: 10, child: SizedBox( height: expandedHeight, width: MediaQuery.of(context).size.width / 2, child: FlutterLogo(), ), ), ), ), ], ); } @override double get maxExtent => expandedHeight; @override double get minExtent => kToolbarHeight; @override bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) => true; }
Resultado
Hecho, ambas tareas están resueltas.
Hay ejemplos en este repositorio .
Conclusión
A menudo nos desesperamos cuando no encontramos ninguna propiedad del widget, pero solo necesita mirar su código fuente para comprender cómo se implementa en Flutter, descubriendo así cómo podemos implementar nuestros propios widgets.