O deslizante em um aplicativo pode ser usado para realizar uma ampla gama de tarefas com apenas um simples deslizar para a direita ou esquerda no bloco. Isso não apenas torna a IU muito amigável, mas também economiza muito tempo na realização de tarefas triviais que, se feitas de outras maneiras, podem ser complicadas e redundantes para o design. Neste artigo, examinaremos o processo de criação de um controle deslizante para seu aplicativo.

Aqui, construiremos um aplicativo simples com blocos que, ao deslizar da esquerda para a direita, arquiva o bloco e, ao deslizar da direita para a esquerda, exclui o bloco. Para fazer isso, siga as etapas abaixo:

  • Adicione a dependência flutter_slidable ao arquivo pubspec.yaml .
  • Importe a dependência para o arquivo main.dart
  • Crie um StatelessWidget para dar ao aplicativo uma estrutura
  • Use um StateFulWidget para adicionar uma página inicial ao aplicativo
  • Use SlidableCntroller para configurar as ações do slide
  • Use FloatingActionButton para atribuir ações aos botões gerados ao deslizar o bloco
  • Use o WidgetBuilder para construir os blocos na página inicial

Vamos discutir as etapas em detalhes.

Adicionando Dependência:

Você pode importar a dependência flutter_slidable no arquivo pubspec.yaml conforme mostrado abaixo:

dependência

Dependência de importação:

Para importar a dependência no arquivo main.dart, use o seguinte:

import 'package:flutter_slidable/flutter_slidable.dart';

Criação da estrutura do aplicativo:

Use um StatelessWidget para fornecer ao aplicativo uma estrutura simples, conforme mostrado abaixo:

class MyApp extends StatelessWidget {
  
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Slidable ',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'GeeksForGeeks'),
    );
  }
}

Criando a página inicial:

Use um StatefulWidget para configurar a página inicial do aplicativo que, no futuro, manterá os blocos que podem ser deslizados em qualquer direção para realizar as tarefas atribuídas a eles, conforme mostrado abaixo:

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  
  final String title;
  
  @override
  _MyHomePageState createState() => _MyHomePageState();
}
  
class _MyHomePageState extends State<MyHomePage> {
  SlidableController slidableController;
  final List<_HomeItem> items = List.generate(
    20,
        (i) => _HomeItem(
      i,
      'Slide Bar $i',
      _getSubtitle(i),
      _getAvatarColor(i),
    ),
  );

Projetando os slides:

Use o SlidableController para configurar os slides para o aplicativo. Isso pode ser feito usando o Construtor Slidable ou o Construtor Slidable.builder . O aplicativo que estamos construindo tem 4 componentes essenciais:

  1. Ações de slides
  2. Um widget de painel de ação de slide
  3. Uma proporção de extensão entre a extensão da ação do slide e a extensão do item.
  4. Uma criança

Para projetar o deslizante, use o seguinte:

Slidable(
  actionPane: SlidableDrawerActionPane(),
  actionExtentRatio: 0.25,
  child: Container(
    color: Colors.white,
    child: ListTile(
      leading: CircleAvatar(
        backgroundColor: Colors.indigoAccent,
        child: Text('$3'),
        foregroundColor: Colors.white,
      ),
      title: Text('Tile $3'),
      subtitle: Text('SlidableDrawerDelegate'),
    ),
  ),
  actions: <Widget>[
    IconSlideAction(
      caption: 'Archive',
      color: Colors.blue,
      icon: Icons.archive,
      onTap: () => _showSnackBar('Archive'),
    ),
    IconSlideAction(
      caption: 'Share',
      color: Colors.indigo,
      icon: Icons.share,
      onTap: () => _showSnackBar('Share'),
    ),
  ],
  secondaryActions: <Widget>[
    IconSlideAction(
      caption: 'More',
      color: Colors.black45,
      icon: Icons.more_horiz,
      onTap: () => _showSnackBar('More'),
    ),
    IconSlideAction(
      caption: 'Delete',
      color: Colors.red,
      icon: Icons.delete,
      onTap: () => _showSnackBar('Delete'),
    ),
  ],
);

Atribuindo ações:

Conforme os slides são passados, um FloatingActionButton aparecerá dependendo da direção do slide. Em ambos os casos, duas ações serão atribuídas a cada furto da seguinte forma:

  • Para slide da esquerda para a direita:
  1. Bloco de arquivo
  2. Compartilhar bloco
  • Para o slide da direita para a esquerda:
  1. Excluir peça
  2. Mais

Para simplificar, iremos apenas atribuir ações ao botão Arquivar bloco e excluir o botão de bloco que irá arquivar e excluir o bloco respectivamente. Para fazer isso, use o seguinte:

@protected
void initState() {
  slidableController = SlidableController(
    onSlideAnimationChanged: handleSlideAnimationChanged,
    onSlideIsOpenChanged: handleSlideIsOpenChanged,
  );
  super.initState();
}
 
Animation<double> _rotationAnimation;
Color _fabColor = Colors.blue;
 
void handleSlideAnimationChanged(Animation<double> slideAnimation) {
  setState(() {
    _rotationAnimation = slideAnimation;
  });
}
 
void handleSlideIsOpenChanged(bool isOpen) {
  setState(() {
    _fabColor = isOpen ? Colors.green : Colors.blue;
  });
}
 
@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text(widget.title),
      backgroundColor: Colors.green,
    ),
    body: Center(
      child: OrientationBuilder(
        builder: (context, orientation) => _buildList(
            context,
            orientation == Orientation.portrait
                ? Axis.vertical
                : Axis.horizontal),
      ),
    ),
    floatingActionButton: FloatingActionButton(
      backgroundColor: _fabColor,
      onPressed: null,
      child: _rotationAnimation == null
          ? Icon(Icons.add)
          : RotationTransition(
        turns: _rotationAnimation,
        child: Icon(Icons.add),
      ),
    ),
  );
}
 
Widget _buildList(BuildContext context, Axis direction) {
  return ListView.builder(
    scrollDirection: direction,
    itemBuilder: (context, index) {
      final Axis slidableDirection =
      direction == Axis.horizontal ? Axis.vertical : Axis.horizontal;
      var item = items[index];
      if (item.index < 8) {
        return _getSlidableWithLists(context, index, slidableDirection);
      } else {
        return _getSlidableWithDelegates(context, index, slidableDirection);
      }
    },
    itemCount: items.length,
  );
}
 
Widget _getSlidableWithLists(
    BuildContext context, int index, Axis direction) {
  final _HomeItem item = items[index];
  return Slidable(
    key: Key(item.title),
    controller: slidableController,
    direction: direction,
    dismissal: SlidableDismissal(
      child: SlidableDrawerDismissal(),
      onDismissed: (actionType) {
        _showSnackBar(
            context,
            actionType == SlideActionType.primary
                ? 'Dismiss Archive'
                : 'Dismiss Delete');
        setState(() {
          items.removeAt(index);
        });
      },
    ),
    actionPane: _getActionPane(item.index),
    actionExtentRatio: 0.25,
    child: direction == Axis.horizontal
        ? VerticalListItem(items[index])
        : HorizontalListItem(items[index]),
    actions: <Widget>[
      IconSlideAction(
        caption: 'Archive',
        color: Colors.blue,
        icon: Icons.archive,
        onTap: () => _showSnackBar(context, 'Archive'),
      ),
      IconSlideAction(
        caption: 'Share',
        color: Colors.indigo,
        icon: Icons.share,
        onTap: () => _showSnackBar(context, 'Share'),
      ),
    ],
    secondaryActions: <Widget>[
      Container(
        height: 800,
        color: Colors.green,
        child: Text('a'),
      ),
      IconSlideAction(
        caption: 'More',
        color: Colors.grey.shade200,
        icon: Icons.more_horiz,
        onTap: () => _showSnackBar(context, 'More'),
        closeOnTap: false,
      ),
      IconSlideAction(
        caption: 'Delete',
        color: Colors.red,
        icon: Icons.delete,
        onTap: () => _showSnackBar(context, 'Delete'),
      ),
    ],
  );
}

Código-fonte completo:

import 'package:flutter/material.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
  
void main() => runApp(MyApp());
  
class MyApp extends StatelessWidget {
  
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Slidable ',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'GeeksForGeeks'),
    );
  }
}
  
class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  
  final String title;
  
  @override
  _MyHomePageState createState() => _MyHomePageState();
}
  
class _MyHomePageState extends State<MyHomePage> {
  SlidableController slidableController;
  final List<_HomeItem> items = List.generate(
    20,
        (i) => _HomeItem(
      i,
      'Slide Bar $i',
      _getSubtitle(i),
      _getAvatarColor(i),
    ),
  );
  
  @protected
  void initState() {
    slidableController = SlidableController(
      onSlideAnimationChanged: handleSlideAnimationChanged,
      onSlideIsOpenChanged: handleSlideIsOpenChanged,
    );
    super.initState();
  }
  
  Animation<double> _rotationAnimation;
  Color _fabColor = Colors.blue;
  
  void handleSlideAnimationChanged(Animation<double> slideAnimation) {
    setState(() {
      _rotationAnimation = slideAnimation;
    });
  }
  
  void handleSlideIsOpenChanged(bool isOpen) {
    setState(() {
      _fabColor = isOpen ? Colors.green : Colors.blue;
    });
  }
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
        backgroundColor: Colors.green,
      ),
      body: Center(
        child: OrientationBuilder(
          builder: (context, orientation) => _buildList(
              context,
              orientation == Orientation.portrait
                  ? Axis.vertical
                  : Axis.horizontal),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        backgroundColor: _fabColor,
        onPressed: null,
        child: _rotationAnimation == null
            ? Icon(Icons.add)
            : RotationTransition(
          turns: _rotationAnimation,
          child: Icon(Icons.add),
        ),
      ),
    );
  }
  
  Widget _buildList(BuildContext context, Axis direction) {
    return ListView.builder(
      scrollDirection: direction,
      itemBuilder: (context, index) {
        final Axis slidableDirection =
        direction == Axis.horizontal ? Axis.vertical : Axis.horizontal;
        var item = items[index];
        if (item.index < 8) {
          return _getSlidableWithLists(context, index, slidableDirection);
        } else {
          return _getSlidableWithDelegates(context, index, slidableDirection);
        }
      },
      itemCount: items.length,
    );
  }
  
  Widget _getSlidableWithLists(
      BuildContext context, int index, Axis direction) {
    final _HomeItem item = items[index];
    //final int t = index;
    return Slidable(
      key: Key(item.title),
      controller: slidableController,
      direction: direction,
      dismissal: SlidableDismissal(
        child: SlidableDrawerDismissal(),
        onDismissed: (actionType) {
          _showSnackBar(
              context,
              actionType == SlideActionType.primary
                  ? 'Dismiss Archive'
                  : 'Dismiss Delete');
          setState(() {
            items.removeAt(index);
          });
        },
      ),
      actionPane: _getActionPane(item.index),
      actionExtentRatio: 0.25,
      child: direction == Axis.horizontal
          ? VerticalListItem(items[index])
          : HorizontalListItem(items[index]),
      actions: <Widget>[
        IconSlideAction(
          caption: 'Archive',
          color: Colors.blue,
          icon: Icons.archive,
          onTap: () => _showSnackBar(context, 'Archive'),
        ),
        IconSlideAction(
          caption: 'Share',
          color: Colors.indigo,
          icon: Icons.share,
          onTap: () => _showSnackBar(context, 'Share'),
        ),
      ],
      secondaryActions: <Widget>[
        Container(
          height: 800,
          color: Colors.green,
          child: Text('a'),
        ),
        IconSlideAction(
          caption: 'More',
          color: Colors.grey.shade200,
          icon: Icons.more_horiz,
          onTap: () => _showSnackBar(context, 'More'),
          closeOnTap: false,
        ),
        IconSlideAction(
          caption: 'Delete',
          color: Colors.red,
          icon: Icons.delete,
          onTap: () => _showSnackBar(context, 'Delete'),
        ),
      ],
    );
  }
  
  Widget _getSlidableWithDelegates(
      BuildContext context, int index, Axis direction) {
    final _HomeItem item = items[index];
  
    return Slidable.builder(
      key: Key(item.title),
      controller: slidableController,
      direction: direction,
      dismissal: SlidableDismissal(
        child: SlidableDrawerDismissal(),
        closeOnCanceled: true,
        onWillDismiss: (item.index != 10)
            ? null
            : (actionType) {
          return showDialog<bool>(
            context: context,
            builder: (context) {
              return AlertDialog(
                title: Text('Delete'),
                content: Text('Item will be deleted'),
                actions: <Widget>[
                  FlatButton(
                    child: Text('Cancel'),
                    onPressed: () => Navigator.of(context).pop(false),
                  ),
                  FlatButton(
                    child: Text('Ok'),
                    onPressed: () => Navigator.of(context).pop(true),
                  ),
                ],
              );
            },
          );
        },
        onDismissed: (actionType) {
          _showSnackBar(
              context,
              actionType == SlideActionType.primary
                  ? 'Dismiss Archive'
                  : 'Dismiss Delete');
          setState(() {
            items.removeAt(index);
          });
        },
      ),
      actionPane: _getActionPane(item.index),
      actionExtentRatio: 0.25,
      child: direction == Axis.horizontal
          ? VerticalListItem(items[index])
          : HorizontalListItem(items[index]),
      actionDelegate: SlideActionBuilderDelegate(
          actionCount: 2,
          builder: (context, index, animation, renderingMode) {
            if (index == 0) {
              return IconSlideAction(
                caption: 'Archive',
                color: renderingMode == SlidableRenderingMode.slide
                    ? Colors.blue.withOpacity(animation.value)
                    : (renderingMode == SlidableRenderingMode.dismiss
                    ? Colors.blue
                    : Colors.green),
                icon: Icons.archive,
                onTap: () async {
                  var state = Slidable.of(context);
                  var dismiss = await showDialog<bool>(
                    context: context,
                    builder: (context) {
                      return AlertDialog(
                        title: Text('Delete'),
                        content: Text('Item will be deleted'),
                        actions: <Widget>[
                          FlatButton(
                            child: Text('Cancel'),
                            onPressed: () => Navigator.of(context).pop(false),
                          ),
                          FlatButton(
                            child: Text('Ok'),
                            onPressed: () => Navigator.of(context).pop(true),
                          ),
                        ],
                      );
                    },
                  );
  
                  if (dismiss) {
                    state.dismiss();
                  }
                },
              );
            } else {
              return IconSlideAction(
                caption: 'Share',
                color: renderingMode == SlidableRenderingMode.slide
                    ? Colors.indigo.withOpacity(animation.value)
                    : Colors.indigo,
                icon: Icons.share,
                onTap: () => _showSnackBar(context, 'Share'),
              );
            }
          }),
      secondaryActionDelegate: SlideActionBuilderDelegate(
          actionCount: 2,
          builder: (context, index, animation, renderingMode) {
            if (index == 0) {
              return IconSlideAction(
                caption: 'More',
                color: renderingMode == SlidableRenderingMode.slide
                    ? Colors.grey.shade200.withOpacity(animation.value)
                    : Colors.grey.shade200,
                icon: Icons.more_horiz,
                onTap: () => _showSnackBar(context, 'More'),
                closeOnTap: false,
              );
            } else {
              return IconSlideAction(
                caption: 'Delete',
                color: renderingMode == SlidableRenderingMode.slide
                    ? Colors.red.withOpacity(animation.value)
                    : Colors.red,
                icon: Icons.delete,
                onTap: () => _showSnackBar(context, 'Delete'),
              );
            }
          }),
    );
  }
  
  static Widget _getActionPane(int index) {
    switch (index % 4) {
      case 0:
        return SlidableBehindActionPane();
      case 1:
        return SlidableStrechActionPane();
      case 2:
        return SlidableScrollActionPane();
      case 3:
        return SlidableDrawerActionPane();
      default:
        return null;
    }
  }
  
  static Color _getAvatarColor(int index) {
    switch (index % 4) {
      case 0:
        return Colors.red;
      case 1:
        return Colors.green;
      case 2:
        return Colors.blue;
      case 3:
        return Colors.indigoAccent;
      default:
        return null;
    }
  }
  
  static String _getSubtitle(int index) {
    switch (index % 4) {
      case 0:
        return ' ';
      case 1:
        return ' ';
      case 2:
        return ' ';
      case 3:
        return ' ';
      default:
        return null;
    }
  }
  
  void _showSnackBar(BuildContext context, String text) {
    Scaffold.of(context).showSnackBar(SnackBar(content: Text(text)));
  }
}
  
class HorizontalListItem extends StatelessWidget {
  HorizontalListItem(this.item);
  final _HomeItem item;
  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.white,
      width: 160.0,
      child: Column(
        mainAxisSize: MainAxisSize.max,
        children: <Widget>[
          Expanded(
            child: CircleAvatar(
              backgroundColor: item.color,
              child: Text('${item.index}'),
              foregroundColor: Colors.white,
            ),
          ),
          Expanded(
            child: Center(
              child: Text(
                item.subtitle,
              ),
            ),
          ),
        ],
      ),
    );
  }
}
  
class VerticalListItem extends StatelessWidget {
  VerticalListItem(this.item);
  final _HomeItem item;
  
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () =>
      Slidable.of(context)?.renderingMode == SlidableRenderingMode.none
          ? Slidable.of(context)?.open()
          : Slidable.of(context)?.close(),
      child: Container(
        color: Colors.white,
        child: ListTile(
          leading: CircleAvatar(
            backgroundColor: item.color,
            child: Text('${item.index}'),
            foregroundColor: Colors.white,
          ),
          title: Text(item.title),
          subtitle: Text(item.subtitle),
        ),
      ),
    );
  }
}
  
class _HomeItem {
  const _HomeItem(
      this.index,
      this.title,
      this.subtitle,
      this.color,
      );
  
  final int index;
  final String title;
  final String subtitle;
  final Color color;
}

Saída:

Quer um ambiente mais competitivo e acelerado para aprender os fundamentos do Android?
Clique aqui para acessar um guia com curadoria exclusiva de nossos especialistas com o objetivo de torná-lo pronto para a indústria em nenhum momento!