Skip to content

Commit

Permalink
Merge pull request #913 from GetStream/desk_web_dev
Browse files Browse the repository at this point in the history
feat(flutter): Desktop support for `stream_chat_flutter`
  • Loading branch information
imtoori authored Jun 27, 2022
2 parents c817110 + 87df3e2 commit 338f907
Show file tree
Hide file tree
Showing 443 changed files with 24,038 additions and 9,996 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/stream_flutter_workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ jobs:
test:
runs-on: macos-latest
if: github.event.pull_request.draft == false
timeout-minutes: 20
timeout-minutes: 30
steps:
- name: "Git Checkout"
uses: actions/checkout@v2
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ abstract class ChatPersistenceClient {
/// Remove a pinned message by message [cids]
Future<void> deletePinnedMessageByCids(List<String> cids);

/// Remove a channel by [cid]
/// Remove a channel by [channelId]
Future<void> deleteChannels(List<String> cids);

/// Updates the message data of a particular channel [cid] with
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ android {
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.example"
minSdkVersion 22
minSdkVersion 23
targetSdkVersion 31
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
Expand Down
294 changes: 259 additions & 35 deletions packages/stream_chat_flutter/example/lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
// ignore_for_file: public_member_api_docs

import 'dart:io';

import 'package:flutter/material.dart';
import 'package:responsive_builder/responsive_builder.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'package:stream_chat_localizations/stream_chat_localizations.dart';

void main() async {
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();

/// Create a new instance of [StreamChatClient] passing the apikey obtained
/// from your project dashboard.
final client = StreamChatClient(
's2dxdhpxd94g',
logLevel: Level.INFO,
logLevel: Level.OFF,
);

/// Set the current user and connect the websocket. In a production
Expand Down Expand Up @@ -63,50 +68,269 @@ class MyApp extends StatelessWidget {
final Channel channel;

@override
Widget build(BuildContext context) => MaterialApp(
theme: ThemeData.light(),
darkTheme: ThemeData.dark(),
supportedLocales: const [
Locale('en'),
Locale('hi'),
Locale('fr'),
Locale('it'),
Locale('es'),
],
localizationsDelegates: GlobalStreamChatLocalizations.delegates,
builder: (context, widget) => StreamChat(
client: client,
child: widget,
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData.light(),
darkTheme: ThemeData.dark(),
supportedLocales: const [
Locale('en'),
Locale('hi'),
Locale('fr'),
Locale('it'),
Locale('es'),
],
localizationsDelegates: GlobalStreamChatLocalizations.delegates,
builder: (context, widget) => StreamChat(
client: client,
child: widget,
),
home: StreamChannel(
channel: channel,
child: const ResponsiveChat(),
),
);
}
}

class ResponsiveChat extends StatelessWidget {
const ResponsiveChat({
super.key,
});

@override
Widget build(BuildContext context) {
return ResponsiveBuilder(
builder: (context, sizingInformation) {
if (sizingInformation.isMobile) {
return ChannelListPage(
onTap: (c) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return StreamChannel(
channel: c,
child: ChannelPage(
onBackPressed: (context) {
Navigator.of(
context,
rootNavigator: true,
).pop();
},
),
);
},
),
);
},
);
}

return const SplitView();
},
breakpoints: const ScreenBreakpoints(
desktop: 550,
tablet: 550,
watch: 300,
),
);
}
}

class SplitView extends StatefulWidget {
const SplitView({
super.key,
});

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

class _SplitViewState extends State<SplitView> {
Channel? selectedChannel;

@override
Widget build(BuildContext context) {
return Flex(
direction: Axis.horizontal,
children: <Widget>[
Flexible(
child: ChannelListPage(
onTap: (channel) {
setState(() {
selectedChannel = channel;
});
},
selectedChannel: selectedChannel,
),
),
home: StreamChannel(
channel: channel,
child: const ChannelPage(),
Flexible(
flex: 2,
child: ClipPath(
child: Scaffold(
body: selectedChannel != null
? StreamChannel(
key: ValueKey(selectedChannel!.cid),
channel: selectedChannel!,
child: const ChannelPage(showBackButton: false),
)
: Center(
child: Text(
'Pick a channel to show the messages 💬',
style: Theme.of(context).textTheme.headline5,
),
),
),
),
),
],
);
}
}

class ChannelListPage extends StatefulWidget {
const ChannelListPage({
super.key,
this.onTap,
this.selectedChannel,
});

final void Function(Channel)? onTap;
final Channel? selectedChannel;

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

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

@override
Widget build(BuildContext context) => Scaffold(
body: StreamChannelListView(
onChannelTap: widget.onTap,
controller: _listController,
itemBuilder: (context, channels, index, defaultWidget) {
return defaultWidget.copyWith(
selected: channels[index] == widget.selectedChannel,
);
},
),
);
}

/// A list of messages sent in the current channel.
///
/// This is implemented using [StreamMessageListView],
/// a widget that provides query
/// functionalities fetching the messages from the api and showing them in a
/// listView.
class ChannelPage extends StatelessWidget {
/// Creates the page that shows the list of messages
class ChannelPage extends StatefulWidget {
const ChannelPage({
super.key,
this.showBackButton = true,
this.onBackPressed,
});

final bool showBackButton;
final void Function(BuildContext)? onBackPressed;

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

class _ChannelPageState extends State<ChannelPage> {
late final messageInputController = StreamMessageInputController();
final focusNode = FocusNode();

@override
Widget build(BuildContext context) => Navigator(
onGenerateRoute: (settings) => MaterialPageRoute(
builder: (context) => Scaffold(
appBar: StreamChannelHeader(
onBackPressed: widget.onBackPressed != null
? () {
widget.onBackPressed!(context);
}
: null,
showBackButton: widget.showBackButton,
),
StreamMessageInput(attachmentLimit: 3),
],
body: Column(
children: <Widget>[
Expanded(
child: StreamMessageListView(
onMessageSwiped:
(Platform.isAndroid || Platform.isIOS) ? reply : null,
threadBuilder: (context, parent) {
return ThreadPage(
parent: parent!,
);
},
messageBuilder:
(context, details, messages, defaultWidget) {
return defaultWidget.copyWith(
onReplyTap: reply,
);
},
),
),
StreamMessageInput(
onQuotedMessageCleared:
messageInputController.clearQuotedMessage,
focusNode: focusNode,
messageInputController: messageInputController,
),
],
),
),
),
);

void reply(Message message) {
messageInputController.quotedMessage = message;
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
focusNode.requestFocus();
});
}

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

class ThreadPage extends StatelessWidget {
const ThreadPage({
super.key,
required this.parent,
});

final Message parent;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: StreamThreadHeader(
parent: parent,
),
body: Column(
children: <Widget>[
Expanded(
child: StreamMessageListView(
parentMessage: parent,
),
),
StreamMessageInput(
messageInputController: StreamMessageInputController(
message: Message(parentId: parent.id),
),
),
],
),
);
}
}
Loading

0 comments on commit 338f907

Please sign in to comment.