7 widgets de Flutter que toda aplicación debería contener

- Andrés Cruz

End Drawer

EndDrawer se usa normalmente para proporcionar navegación a otras páginas, pero también podemos usarlo como pantalla de filtro.

class EndDrawerScreen extends StatefulWidget {
  const EndDrawerScreen({Key? key}) : super(key: key);

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

class _EndDrawerScreenState extends State<EndDrawerScreen> {
  late ThemeData theme;

  GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: scaffoldKey,
      appBar: AppBar(
        title: const Text("End Drawer"),
      ),
      endDrawer: endDrawer(),
      resizeToAvoidBottomInset: false,
      body: const Center(child: Text("End Drawer Screen")),
    );
  }

  Widget endDrawer() {
    return SafeArea(
      child: Container(
        margin: const EdgeInsets.all(16),
        width: 300,
        decoration: const BoxDecoration(
          borderRadius: BorderRadius.all(Radius.circular(8)),
          color: Colors.green,
        ),
        child: Drawer(
          child:
              Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
            Container(
              padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
              color: Colors.lightBlue,
              child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  const Center(
                    child: Text(
                      "Filter",
                      style: TextStyle(
                        color: Colors.white,
                        fontSize: 20,
                      ),
                    ),
                  ),
                  IconButton(
                      onPressed: () {
                        Navigator.pop(context);
                      },
                      icon: const Icon(
                        Icons.cancel,
                        color: Colors.white,
                      ))
                ],
              ),
            ),
            Container(
              padding: EdgeInsets.all(8),
              child: const Text(
                "Select your category",
                style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600),
              ),
            ),
            Container(
              padding: EdgeInsets.all(8),
              child: Wrap(
                spacing: 4,
                runSpacing: 4,
                children: categories(),
              ),
            ),
          ]),
        ),
      ),
    );
  }

  List<Widget> categories() {
    List<String> typesList =
        List<String>.generate(10, (index) => "Category $index");

    List<Widget> categories = [];
    for (var item in typesList) {
      categories.add(Container(
        decoration: BoxDecoration(
          borderRadius: const BorderRadius.all(Radius.circular(12)),
          color: Colors.grey.shade300,
        ),
        padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 8),
        child: Text(
          item,
        ),
      ));
    }
    return categories;
  }
}

Loading Button

Prefiero usar un botón de carga animado como este en lugar de CircularProgressIndicator porque muestro un estado de error o éxito y hago que el usuario no pueda presionar el botón nuevamente hasta que solucione el error.

class CustomLoadingButton extends StatelessWidget {
 final RoundedLoadingButtonController controller;
 final VoidCallback? onPressed;
 final EdgeInsetsGeometry? padding;
 final double? elevation;
 final double borderRadiusAll;
 final Color? backgroundColor;
 final Color? successColor;
 final Color? errorColor;
 final Color? splashColor;
 final Widget child;
 final double width;
 final double height;
 const CustomLoadingButton(
     {Key? key,
     required this.controller,
     this.onPressed,
     required this.child,
     this.padding,
     this.borderRadiusAll = 0,
     this.backgroundColor = Colors.green,
     this.successColor = Colors.green,
     this.errorColor = Colors.red,
     this.elevation = 4,
     this.splashColor,
     this.height = 35,
     this.width = 300})
     : super(key: key);
 @override
 Widget build(BuildContext context) {
   return RoundedLoadingButton(
     color: backgroundColor,
     successColor: successColor,
     errorColor: errorColor,
     controller: controller,
     onPressed: onPressed,
     valueColor: Colors.white,
     borderRadius: borderRadiusAll,
     child: child,
     height: height,
     width: width,
   );
 }
}
RoundedLoadingButtonController btnController = RoundedLoadingButtonController();
TextEditingController textEditingController = TextEditingController();
final formKey = GlobalKey<FormState>();
@override
Widget build(BuildContext context) {
 return Container(
   padding: EdgeInsets.all(16),
   child: Form(
     key: formKey,
     child: Column(
       crossAxisAlignment: CrossAxisAlignment.start,
       children: [
         TextFormField(
           controller: textEditingController,
           decoration: InputDecoration(
             labelText: "Add your number",
           ),
           validator: (value) {
             if (value != null && value.isNotEmpty) {
               if (RegExp(r'^[a-z]+$').hasMatch(value)) {
                 return "Just numbers allowed";
               }
               return null;
             } else {
               return "Please enter your number";
             }
           },
         ),
         SizedBox(
           height: 16,
         ),
         Center(
           child: RLoadingButton(
             controller: btnController,
             onPressed: () {
               Future.delayed(Duration(seconds: 1)).then((value) {
                 if (formKey.currentState!.validate()) {
                   btnController.success();
                 } else {
                   btnController.error();
                 }
               });
             },
             backgroundColor: Colors.blue,
             child: Text("Add"),
           ),
         )
       ],
     ),
     onChanged: () {
       if (btnController.currentState == ButtonState.success || btnController.currentState == ButtonState.error) {
         btnController.reset();
       }
       formKey.currentState!.validate();
     },
   ),
 );
}

Stepper

Stepper es un widget de material que muestra el progreso a través de una secuencia de pasos. Los pasos son particularmente útiles en el caso de formularios en los que un paso requiere completar otro, o donde es necesario completar varios pasos para enviar el formulario completo.

Puedes usarlo en dirección vertical y horizontal.

Modal Bottom Sheet

Un Modal Bottom Sheet es una alternativa a un menú o cuadro de diálogo y evita que el usuario interactúe con el resto de la aplicación.

Es como un cuadro de diálogo que se abre desde abajo y podemos usarlo para crear espacio para más contenido en nuestras aplicaciones.

class BottomSheetScreen extends StatefulWidget {
  const BottomSheetScreen({Key? key}) : super(key: key);

  @override
  State<BottomSheetScreen> createState() => _BottomSheetScreenState();
}

class _BottomSheetScreenState extends State<BottomSheetScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text(
          'Modal Bottom Sheet',
        ),
      ),
      body: Container(
        alignment: Alignment.center,
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text(
              "MODAL BOTTOM SHEET",
            ),
            const SizedBox(
              height: 20,
            ),
            ElevatedButton(
              onPressed: () {
                showModalBottomSheet(
                    context: context,
                    builder: (context) {
                      return Column(
                        mainAxisSize: MainAxisSize.min,
                        children: <Widget>[
                          ListTile(
                            leading: const Icon(Icons.copy),
                            title: const Text('Copy'),
                            onTap: () {
                              Navigator.pop(context);
                            },
                          ),
                          ListTile(
                            leading: const Icon(Icons.save),
                            title: const Text('Save'),
                            onTap: () {
                              Navigator.pop(context);
                            },
                          ),
                          ListTile(
                            leading: const Icon(Icons.share),
                            title: const Text('Share'),
                            onTap: () {
                              Navigator.pop(context);
                            },
                          ),
                        ],
                      );
                    });
              },
              child: const Text(
                'Click Me',
              ),
            ),
          ],
        ),
      ),
    );
  }
}

Tooltip

Los Tooltips proporcionan una etiqueta de texto que identifica el elemento, como una descripción de su función.

Los Tooltips sobre herramientas mejoran la accesibilidad de los widgets visuales al proporcionar una representación textual del widget, que, por ejemplo, puede ser vocalizada por un lector de pantalla.

Los Tooltips sobre herramientas se utilizan para mostrar una etiqueta de texto simple y no se recomienda mostrar información e imágenes ricas en ella.

Tooltip(
 message: "Add new item",
 child: Icon(Icons.add_circle_rounded, size: 32, color: Colors.blue,),
),

Slide Action

Puede utilizar Slide Action para hacer que el usuario confirme o realice una acción.

Rich Text
Este widget muestra texto que utiliza varios estilos diferentes.
RichText(
 text: const TextSpan(
   text: 'Don\'t have an account yet? ',
   style: TextStyle(
     color: Colors.black,
     fontSize: 18,
   ),
   children: [
     TextSpan(
       text: 'Sign',
       style: TextStyle(
         color: Colors.blue,
         fontSize: 18,
         decoration: TextDecoration.underline,
         fontWeight: FontWeight.w800,
       ),
     ),
     TextSpan(
       text: ' ',
     ),
     TextSpan(
       text: 'Up',
       style: TextStyle(
         color: Colors.red,
         fontSize: 18,
         decoration: TextDecoration.underline,
         fontWeight: FontWeight.w800,
       ),
     ),
   ],
 ),
)

Artículo original:

https://medium.com/@ahmad.hamoush.785/8-flutter-widgets-every-app-should-contain-5c6129a6928b

Andrés Cruz

Desarrollo con Laravel, Django, Flask, CodeIgniter, HTML5, CSS3, MySQL, JavaScript, Vue, Android, iOS, Flutter

Andrés Cruz en Udemy

Acepto recibir anuncios de interes sobre este Blog.