Skip to main content
Version: v4

Slidable Channel List Preview

Slidable Channel List Preview

Introduction

The default slidable behavior within the channel list has been removed in v4 of the Stream Chat Flutter SDK. This guide will show you how you can easily add this functionality yourself.

Please see our full v4 migration guide if you're migrating from an earlier version of the Stream Chat Flutter SDK.

Slidable demo

Prerequisites

This guide assumes you are familiar with the Stream Chat SDK. If you're new to Stream Chat Flutter, we recommend looking at our getting started tutorial.

Dependencies:

dependencies:
flutter:
sdk: flutter
stream_chat_flutter: ^4.0.0
flutter_slidable: ^1.2.0

⚠️ Note: The examples shown in this guide use the above packages and versions.

Example Code - Custom Stream Channel Item Builder

In this example, you are doing a few important things in the ChannelListPage widget. You're:

  • Using the flutter_slidable package to easily add slide functionality.
  • Passing in the itemBuilder argument for the StreamChannelListView widget. This gives access to the current BuildContext, Channel, and StreamChannelListTile, and allows you to create, or customize, the stream channel list tiles.
  • Returning a Slidable widget with two CustomSlidableAction widgets - to delete a channel and show more options. These widgets come from the flutter_slidable package.
  • Adding onPressed behaviour to call showConfirmationDialog and showChannelInfoModalBottomSheet. These methods come from the stream_chat_flutter package. They have a few different on-tap callbacks you can supply, for example, onViewInfoTap. Alternatively, you can create custom dialog screens from scratch.
  • Using the StreamChannelListController to perform actions, such as, deleteChannel.
import 'package:flutter/material.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';

void main() async {
final client = StreamChatClient(
's2dxdhpxd94g',
);

await client.connectUser(
User(id: 'super-band-9'),
'''eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3VwZXItYmFuZC05In0.0L6lGoeLwkz0aZRUcpZKsvaXtNEDHBcezVTZ0oPq40A''',
);

runApp(
MyApp(
client: client,
),
);
}

class MyApp extends StatelessWidget {
const MyApp({
Key? key,
required this.client,
}) : super(key: key);

final StreamChatClient client;

@override
Widget build(BuildContext context) {
return MaterialApp(
builder: (context, child) => StreamChat(
client: client,
child: child,
),
home: ChannelListPage(
client: client,
),
);
}
}

class ChannelListPage extends StatefulWidget {
const ChannelListPage({
Key? key,
required this.client,
}) : super(key: key);

final StreamChatClient client;

@override
State<ChannelListPage> createState() => _ChannelListPageState();
}

class _ChannelListPageState extends State<ChannelListPage> {
late final _controller = StreamChannelListController(
client: widget.client,
filter: Filter.in_(
'members',
[StreamChat.of(context).currentUser!.id],
),
channelStateSort: const [SortOption('last_message_at')],
);

@override
void dispose() {
_controller.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) => Scaffold(
body: SlidableAutoCloseBehavior(
child: RefreshIndicator(
onRefresh: _controller.refresh,
child: StreamChannelListView(
controller: _controller,
itemBuilder: (context, channel, tile) {
final chatTheme = StreamChatTheme.of(context);
final backgroundColor = chatTheme.colorTheme.inputBg;
final canDeleteChannel = channel.ownCapabilities
.contains(PermissionType.deleteChannel);
return Slidable(
groupTag: 'channels-actions',
endActionPane: ActionPane(
extentRatio: canDeleteChannel ? 0.40 : 0.20,
motion: const BehindMotion(),
children: [
CustomSlidableAction(
onPressed: (_) {
showChannelInfoModalBottomSheet(
context: context,
channel: channel,
onViewInfoTap: () {
Navigator.pop(context);
// Navigate to info screen
},
);
},
backgroundColor: backgroundColor,
child: const Icon(Icons.more_horiz),
),
if (canDeleteChannel)
CustomSlidableAction(
backgroundColor: backgroundColor,
child: StreamSvgIcon.delete(
color: chatTheme.colorTheme.accentError,
),
onPressed: (_) async {
final res = await showConfirmationDialog(
context,
title: 'Delete Conversation',
question:
'Are you sure you want to delete this conversation?',
okText: 'Delete',
cancelText: 'Cancel',
icon: StreamSvgIcon.delete(
color: chatTheme.colorTheme.accentError,
),
);
if (res == true) {
await _controller.deleteChannel(channel);
}
},
),
],
),
child: tile,
);
},
onChannelTap: (channel) => Navigator.push(
context,
MaterialPageRoute(
builder: (_) => StreamChannel(
channel: channel,
child: const ChannelPage(),
),
),
),
),
),
),
);
}

class ChannelPage extends StatelessWidget {
const ChannelPage({
Key? key,
}) : super(key: key);

@override
Widget build(BuildContext context) => Scaffold(
appBar: const StreamChannelHeader(),
body: Column(
children: const <Widget>[
Expanded(
child: StreamMessageListView(),
),
StreamMessageInput(),
],
),
);
}

The above is the complete sample, and all you need for a basic implementation.

Did you find this page helpful?