Merge pull request #232 from GetStream/feature/ui-split
Feature/UI split
@@ -1,3 +1,4 @@
|
|||||||
org.gradle.jvmargs=-Xmx1536M
|
org.gradle.jvmargs=-Xmx1536M
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
android.enableJetifier=true
|
android.enableJetifier=true
|
||||||
|
android.enableR8=true
|
||||||
|
|||||||
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
|||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ class _MessageViewState extends State<MessageView> {
|
|||||||
/// Convenience method for scrolling the list view when a new message is sent.
|
/// Convenience method for scrolling the list view when a new message is sent.
|
||||||
void _updateList() {
|
void _updateList() {
|
||||||
_scrollController.animateTo(
|
_scrollController.animateTo(
|
||||||
_scrollController.position.maxScrollExtent,
|
0,
|
||||||
duration: const Duration(milliseconds: 200),
|
duration: const Duration(milliseconds: 200),
|
||||||
curve: Curves.easeOut,
|
curve: Curves.easeOut,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
## 1.0.0-rc
|
||||||
|
|
||||||
|
- Integrated new-ui, split package functionality,
|
||||||
|
|
||||||
## 0.2.20+2
|
## 0.2.20+2
|
||||||
|
|
||||||
- Added `shouldAddChannel` to ChannelsBloc in order to check if a channel has to be added to the list when a new message arrives
|
- Added `shouldAddChannel` to ChannelsBloc in order to check if a channel has to be added to the list when a new message arrives
|
||||||
|
|||||||
@@ -60,13 +60,9 @@ If it seems related to the [flutter file picker plugin](https://github.com/migue
|
|||||||
|
|
||||||
## Docs
|
## Docs
|
||||||
|
|
||||||
### Business logic components
|
This package provides UI components required for integrating Stream Chat into your application.
|
||||||
|
Alternatively, you may use the core package (stream_chat_flutter_core) which allows more customisation and provides business logic but no UI components.
|
||||||
We provide 3 Widgets dedicated to business logic and state management:
|
If you require the maximum amount of control over the API, please use the low level client package: stream_chat.
|
||||||
|
|
||||||
- [StreamChat](https://pub.dev/documentation/stream_chat_flutter/latest/stream_chat_flutter/StreamChat-class.html)
|
|
||||||
- [StreamChannel](https://pub.dev/documentation/stream_chat_flutter/latest/stream_chat_flutter/StreamChannel-class.html)
|
|
||||||
- [ChannelsBloc](https://pub.dev/documentation/stream_chat_flutter/0.2.0-alpha+2/stream_chat_flutter/ChannelsBloc-class.html)
|
|
||||||
|
|
||||||
### UI Components
|
### UI Components
|
||||||
|
|
||||||
|
|||||||
@@ -135,287 +135,284 @@ class _NewChatScreenState extends State<NewChatScreen> {
|
|||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
),
|
),
|
||||||
body: ValueListenableBuilder<ConnectionStatus>(
|
body: ValueListenableBuilder<ConnectionStatus>(
|
||||||
valueListenable: StreamChat.of(context).client.wsConnectionStatus,
|
valueListenable: StreamChat.of(context).client.wsConnectionStatus,
|
||||||
builder: (context, status, _) {
|
builder: (context, status, _) {
|
||||||
String statusString = '';
|
String statusString = '';
|
||||||
bool showStatus = true;
|
bool showStatus = true;
|
||||||
|
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case ConnectionStatus.connected:
|
case ConnectionStatus.connected:
|
||||||
statusString = 'Connected';
|
statusString = 'Connected';
|
||||||
showStatus = false;
|
showStatus = false;
|
||||||
break;
|
break;
|
||||||
case ConnectionStatus.connecting:
|
case ConnectionStatus.connecting:
|
||||||
statusString = 'Reconnecting...';
|
statusString = 'Reconnecting...';
|
||||||
break;
|
break;
|
||||||
case ConnectionStatus.disconnected:
|
case ConnectionStatus.disconnected:
|
||||||
statusString = 'Disconnected';
|
statusString = 'Disconnected';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return InfoTile(
|
return InfoTile(
|
||||||
showMessage: showStatus,
|
showMessage: showStatus,
|
||||||
tileAnchor: Alignment.topCenter,
|
tileAnchor: Alignment.topCenter,
|
||||||
childAnchor: Alignment.topCenter,
|
childAnchor: Alignment.topCenter,
|
||||||
message: statusString,
|
message: statusString,
|
||||||
child: StreamChannel(
|
child: StreamChannel(
|
||||||
showLoading: false,
|
showLoading: false,
|
||||||
channel: channel,
|
channel: channel,
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
ChipsInputTextField<User>(
|
ChipsInputTextField<User>(
|
||||||
key: _chipInputTextFieldStateKey,
|
key: _chipInputTextFieldStateKey,
|
||||||
controller: _controller,
|
controller: _controller,
|
||||||
focusNode: _searchFocusNode,
|
focusNode: _searchFocusNode,
|
||||||
chipBuilder: (context, user) {
|
chipBuilder: (context, user) {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
_chipInputTextFieldState.removeItem(user);
|
_chipInputTextFieldState.removeItem(user);
|
||||||
_searchFocusNode.requestFocus();
|
_searchFocusNode.requestFocus();
|
||||||
},
|
},
|
||||||
child: Stack(
|
child: Stack(
|
||||||
alignment: AlignmentDirectional.centerStart,
|
alignment: AlignmentDirectional.centerStart,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: StreamChatTheme.of(context)
|
||||||
|
.colorTheme
|
||||||
|
.greyGainsboro,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.only(left: 24),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(8, 4, 12, 4),
|
||||||
|
child: Text(
|
||||||
|
user.name,
|
||||||
|
maxLines: 1,
|
||||||
|
style: TextStyle(
|
||||||
|
color: StreamChatTheme.of(context)
|
||||||
|
.colorTheme
|
||||||
|
.black,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
foregroundDecoration: BoxDecoration(
|
||||||
|
color: StreamChatTheme.of(context)
|
||||||
|
.colorTheme
|
||||||
|
.overlay,
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
),
|
||||||
|
child: UserAvatar(
|
||||||
|
showOnlineStatus: false,
|
||||||
|
user: user,
|
||||||
|
constraints: BoxConstraints.tightFor(
|
||||||
|
height: 24,
|
||||||
|
width: 24,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
StreamSvgIcon.close(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onChipAdded: (user) {
|
||||||
|
setState(() => _selectedUsers.add(user));
|
||||||
|
},
|
||||||
|
onChipRemoved: (user) {
|
||||||
|
setState(() => _selectedUsers.remove(user));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
if (!_isSearchActive && !_selectedUsers.isNotEmpty)
|
||||||
|
Container(
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.pushNamed(
|
||||||
|
context,
|
||||||
|
Routes.NEW_GROUP_CHAT,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||||
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Container(
|
StreamNeumorphicButton(
|
||||||
decoration: BoxDecoration(
|
child: Center(
|
||||||
color: StreamChatTheme.of(context)
|
child: StreamSvgIcon.contacts(
|
||||||
.colorTheme
|
color: StreamChatTheme.of(context)
|
||||||
.greyGainsboro,
|
.colorTheme
|
||||||
borderRadius: BorderRadius.circular(12),
|
.accentBlue,
|
||||||
),
|
size: 24,
|
||||||
padding: const EdgeInsets.only(left: 24),
|
|
||||||
child: Padding(
|
|
||||||
padding:
|
|
||||||
const EdgeInsets.fromLTRB(8, 4, 12, 4),
|
|
||||||
child: Text(
|
|
||||||
user.name,
|
|
||||||
maxLines: 1,
|
|
||||||
style: TextStyle(
|
|
||||||
color: StreamChatTheme.of(context)
|
|
||||||
.colorTheme
|
|
||||||
.black,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Container(
|
SizedBox(width: 8),
|
||||||
foregroundDecoration: BoxDecoration(
|
Text(
|
||||||
color: StreamChatTheme.of(context)
|
'Create a Group',
|
||||||
.colorTheme
|
style: StreamChatTheme.of(context)
|
||||||
.overlay,
|
.textTheme
|
||||||
shape: BoxShape.circle,
|
.bodyBold,
|
||||||
),
|
|
||||||
child: UserAvatar(
|
|
||||||
showOnlineStatus: false,
|
|
||||||
user: user,
|
|
||||||
constraints: BoxConstraints.tightFor(
|
|
||||||
height: 24,
|
|
||||||
width: 24,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
StreamSvgIcon.close(),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
},
|
),
|
||||||
onChipAdded: (user) {
|
|
||||||
setState(() => _selectedUsers.add(user));
|
|
||||||
},
|
|
||||||
onChipRemoved: (user) {
|
|
||||||
setState(() => _selectedUsers.remove(user));
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
if (!_isSearchActive && !_selectedUsers.isNotEmpty)
|
if (_showUserList)
|
||||||
Container(
|
Container(
|
||||||
child: InkWell(
|
width: double.maxFinite,
|
||||||
onTap: () {
|
decoration: BoxDecoration(
|
||||||
Navigator.pushNamed(
|
gradient:
|
||||||
context,
|
StreamChatTheme.of(context).colorTheme.bgGradient,
|
||||||
Routes.NEW_GROUP_CHAT,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
StreamNeumorphicButton(
|
|
||||||
child: Center(
|
|
||||||
child: StreamSvgIcon.contacts(
|
|
||||||
color: StreamChatTheme.of(context)
|
|
||||||
.colorTheme
|
|
||||||
.accentBlue,
|
|
||||||
size: 24,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(width: 8),
|
|
||||||
Text(
|
|
||||||
'Create a Group',
|
|
||||||
style: StreamChatTheme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.bodyBold,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
if (_showUserList)
|
child: Padding(
|
||||||
Container(
|
padding: const EdgeInsets.symmetric(
|
||||||
width: double.maxFinite,
|
vertical: 8,
|
||||||
decoration: BoxDecoration(
|
horizontal: 8,
|
||||||
gradient:
|
|
||||||
StreamChatTheme.of(context).colorTheme.bgGradient,
|
|
||||||
),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
vertical: 8,
|
|
||||||
horizontal: 8,
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
_isSearchActive
|
|
||||||
? "Matches for \"$_userNameQuery\""
|
|
||||||
: 'On the platform',
|
|
||||||
style: StreamChatTheme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.footnote
|
|
||||||
.copyWith(
|
|
||||||
color: StreamChatTheme.of(context)
|
|
||||||
.colorTheme
|
|
||||||
.black
|
|
||||||
.withOpacity(.5))),
|
|
||||||
),
|
),
|
||||||
|
child: Text(
|
||||||
|
_isSearchActive
|
||||||
|
? "Matches for \"$_userNameQuery\""
|
||||||
|
: 'On the platform',
|
||||||
|
style: StreamChatTheme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.footnote
|
||||||
|
.copyWith(
|
||||||
|
color: StreamChatTheme.of(context)
|
||||||
|
.colorTheme
|
||||||
|
.black
|
||||||
|
.withOpacity(.5))),
|
||||||
),
|
),
|
||||||
Expanded(
|
),
|
||||||
child: _showUserList
|
Expanded(
|
||||||
? GestureDetector(
|
child: _showUserList
|
||||||
behavior: HitTestBehavior.opaque,
|
? GestureDetector(
|
||||||
onPanDown: (_) =>
|
behavior: HitTestBehavior.opaque,
|
||||||
FocusScope.of(context).unfocus(),
|
onPanDown: (_) => FocusScope.of(context).unfocus(),
|
||||||
child: UsersBloc(
|
child: UsersBloc(
|
||||||
child: UserListView(
|
child: UserListView(
|
||||||
selectedUsers: _selectedUsers,
|
selectedUsers: _selectedUsers,
|
||||||
groupAlphabetically:
|
groupAlphabetically:
|
||||||
_isSearchActive ? false : true,
|
_isSearchActive ? false : true,
|
||||||
onUserTap: (user, _) {
|
onUserTap: (user, _) {
|
||||||
_controller.clear();
|
_controller.clear();
|
||||||
if (!_selectedUsers.contains(user)) {
|
if (!_selectedUsers.contains(user)) {
|
||||||
_chipInputTextFieldState
|
_chipInputTextFieldState
|
||||||
..addItem(user)
|
..addItem(user)
|
||||||
..pauseItemAddition();
|
..pauseItemAddition();
|
||||||
} else {
|
} else {
|
||||||
_chipInputTextFieldState.removeItem(user);
|
_chipInputTextFieldState.removeItem(user);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
pagination: PaginationParams(
|
pagination: PaginationParams(
|
||||||
limit: 25,
|
limit: 25,
|
||||||
),
|
),
|
||||||
filter: {
|
filter: {
|
||||||
if (_userNameQuery.isNotEmpty)
|
if (_userNameQuery.isNotEmpty)
|
||||||
'name': {
|
'name': {
|
||||||
r'$autocomplete': _userNameQuery,
|
r'$autocomplete': _userNameQuery,
|
||||||
},
|
|
||||||
'id': {
|
|
||||||
r'$ne': StreamChat.of(context).user.id,
|
|
||||||
},
|
},
|
||||||
|
'id': {
|
||||||
|
r'$ne': StreamChat.of(context).user.id,
|
||||||
},
|
},
|
||||||
sort: [
|
},
|
||||||
SortOption(
|
sort: [
|
||||||
'name',
|
SortOption(
|
||||||
direction: 1,
|
'name',
|
||||||
),
|
direction: 1,
|
||||||
],
|
),
|
||||||
emptyBuilder: (_) {
|
],
|
||||||
return LayoutBuilder(
|
emptyBuilder: (_) {
|
||||||
builder: (context, viewportConstraints) {
|
return LayoutBuilder(
|
||||||
return SingleChildScrollView(
|
builder: (context, viewportConstraints) {
|
||||||
physics:
|
return SingleChildScrollView(
|
||||||
AlwaysScrollableScrollPhysics(),
|
physics:
|
||||||
child: ConstrainedBox(
|
AlwaysScrollableScrollPhysics(),
|
||||||
constraints: BoxConstraints(
|
child: ConstrainedBox(
|
||||||
minHeight:
|
constraints: BoxConstraints(
|
||||||
viewportConstraints.maxHeight,
|
minHeight:
|
||||||
),
|
viewportConstraints.maxHeight,
|
||||||
child: Center(
|
),
|
||||||
child: Column(
|
child: Center(
|
||||||
children: [
|
child: Column(
|
||||||
Padding(
|
children: [
|
||||||
padding:
|
Padding(
|
||||||
const EdgeInsets.all(
|
padding:
|
||||||
24),
|
const EdgeInsets.all(24),
|
||||||
child: StreamSvgIcon.search(
|
child: StreamSvgIcon.search(
|
||||||
size: 96,
|
size: 96,
|
||||||
color: Colors.grey,
|
color: Colors.grey,
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Text(
|
),
|
||||||
'No user matches these keywords...',
|
Text(
|
||||||
style: StreamChatTheme.of(
|
'No user matches these keywords...',
|
||||||
context)
|
style: StreamChatTheme.of(
|
||||||
.textTheme
|
context)
|
||||||
.footnote
|
.textTheme
|
||||||
.copyWith(
|
.footnote
|
||||||
color: StreamChatTheme
|
.copyWith(
|
||||||
.of(context)
|
color: StreamChatTheme
|
||||||
.colorTheme
|
.of(context)
|
||||||
.black
|
.colorTheme
|
||||||
.withOpacity(
|
.black
|
||||||
.5)),
|
.withOpacity(.5)),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
},
|
);
|
||||||
);
|
},
|
||||||
},
|
);
|
||||||
),
|
},
|
||||||
),
|
),
|
||||||
)
|
|
||||||
: FutureBuilder<bool>(
|
|
||||||
future: channel.initialized,
|
|
||||||
builder: (context, snapshot) {
|
|
||||||
if (snapshot.data == true) {
|
|
||||||
return MessageListView();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Center(
|
|
||||||
child: Text(
|
|
||||||
'No chats here yet...',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
color: StreamChatTheme.of(context)
|
|
||||||
.colorTheme
|
|
||||||
.black
|
|
||||||
.withOpacity(.5),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
)
|
||||||
MessageInput(
|
: FutureBuilder<bool>(
|
||||||
focusNode: _messageInputFocusNode,
|
future: channel.initialized,
|
||||||
preMessageSending: (message) async {
|
builder: (context, snapshot) {
|
||||||
await channel.watch();
|
if (snapshot.data == true) {
|
||||||
return message;
|
return MessageListView();
|
||||||
},
|
}
|
||||||
onMessageSent: (m) {
|
|
||||||
Navigator.pushNamedAndRemoveUntil(
|
return Center(
|
||||||
context,
|
child: Text(
|
||||||
Routes.CHANNEL_PAGE,
|
'No chats here yet...',
|
||||||
ModalRoute.withName(Routes.HOME),
|
style: TextStyle(
|
||||||
arguments: ChannelPageArgs(channel: channel),
|
fontSize: 12,
|
||||||
);
|
color: StreamChatTheme.of(context)
|
||||||
},
|
.colorTheme
|
||||||
),
|
.black
|
||||||
],
|
.withOpacity(.5),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
MessageInput(
|
||||||
|
focusNode: _messageInputFocusNode,
|
||||||
|
preMessageSending: (message) async {
|
||||||
|
await channel.watch();
|
||||||
|
return message;
|
||||||
|
},
|
||||||
|
onMessageSent: (m) {
|
||||||
|
Navigator.pushNamedAndRemoveUntil(
|
||||||
|
context,
|
||||||
|
Routes.CHANNEL_PAGE,
|
||||||
|
ModalRoute.withName(Routes.HOME),
|
||||||
|
arguments: ChannelPageArgs(channel: channel),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
}),
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:stream_chat/stream_chat.dart';
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
|
|
||||||
import '../stream_chat_flutter.dart';
|
import '../stream_chat_flutter.dart';
|
||||||
|
|
||||||
class AttachmentError extends StatelessWidget {
|
class AttachmentError extends StatelessWidget {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:stream_chat/stream_chat.dart';
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
|
|
||||||
import 'stream_chat_theme.dart';
|
import 'stream_chat_theme.dart';
|
||||||
import 'utils.dart';
|
import 'utils.dart';
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:stream_chat_flutter/src/lazy_load_scroll_view.dart';
|
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
class ChannelFileDisplayScreen extends StatefulWidget {
|
class ChannelFileDisplayScreen extends StatefulWidget {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:stream_chat/stream_chat.dart';
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
import 'package:stream_chat_flutter/src/back_button.dart';
|
import 'package:stream_chat_flutter/src/back_button.dart';
|
||||||
import 'package:stream_chat_flutter/src/channel_info.dart';
|
import 'package:stream_chat_flutter/src/channel_info.dart';
|
||||||
import 'package:stream_chat_flutter/src/channel_name.dart';
|
import 'package:stream_chat_flutter/src/channel_name.dart';
|
||||||
@@ -9,7 +9,6 @@ import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
|
|||||||
import '../stream_chat_flutter.dart';
|
import '../stream_chat_flutter.dart';
|
||||||
import './channel_name.dart';
|
import './channel_name.dart';
|
||||||
import 'channel_image.dart';
|
import 'channel_image.dart';
|
||||||
import 'stream_channel.dart';
|
|
||||||
|
|
||||||
/// 
|
/// 
|
||||||
/// 
|
/// 
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:stream_chat/stream_chat.dart';
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
import 'package:stream_chat_flutter/src/group_image.dart';
|
import 'package:stream_chat_flutter/src/group_image.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import 'dart:ui';
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:stream_chat/stream_chat.dart';
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
import 'package:stream_chat_flutter/src/stream_neumorphic_button.dart';
|
import 'package:stream_chat_flutter/src/stream_neumorphic_button.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
|
|||||||
@@ -5,16 +5,13 @@ import 'package:flutter/foundation.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||||
import 'package:shimmer/shimmer.dart';
|
import 'package:shimmer/shimmer.dart';
|
||||||
import 'package:stream_chat/stream_chat.dart';
|
|
||||||
import 'package:stream_chat_flutter/src/channels_bloc.dart';
|
|
||||||
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
||||||
import 'package:stream_chat_flutter/src/utils.dart';
|
import 'package:stream_chat_flutter/src/utils.dart';
|
||||||
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
|
|
||||||
import '../stream_chat_flutter.dart';
|
import '../stream_chat_flutter.dart';
|
||||||
import 'channel_bottom_sheet.dart';
|
import 'channel_bottom_sheet.dart';
|
||||||
import 'channel_preview.dart';
|
import 'channel_preview.dart';
|
||||||
import 'stream_channel.dart';
|
|
||||||
import 'stream_chat.dart';
|
|
||||||
|
|
||||||
/// Callback called when tapping on a channel
|
/// Callback called when tapping on a channel
|
||||||
typedef ChannelTapCallback = void Function(Channel, Widget);
|
typedef ChannelTapCallback = void Function(Channel, Widget);
|
||||||
@@ -158,168 +155,163 @@ class _ChannelListViewState extends State<ChannelListView>
|
|||||||
with WidgetsBindingObserver {
|
with WidgetsBindingObserver {
|
||||||
final ScrollController _scrollController = ScrollController();
|
final ScrollController _scrollController = ScrollController();
|
||||||
final SlidableController _slideController = SlidableController();
|
final SlidableController _slideController = SlidableController();
|
||||||
|
final ChannelListController _channelListController = ChannelListController();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final channelsBloc = ChannelsBloc.of(context);
|
var child = ChannelListCore(
|
||||||
|
channelListController: _channelListController,
|
||||||
|
listBuilder: (context, list) {
|
||||||
|
return _buildListView(list);
|
||||||
|
},
|
||||||
|
emptyBuilder: (BuildContext context) {
|
||||||
|
return _buildEmptyWidget();
|
||||||
|
},
|
||||||
|
errorBuilder: (Error error) {
|
||||||
|
return _buildErrorWidget(context);
|
||||||
|
},
|
||||||
|
loadingBuilder: (BuildContext context) {
|
||||||
|
return _buildLoadingWidget();
|
||||||
|
},
|
||||||
|
pagination: widget.pagination,
|
||||||
|
options: widget.options,
|
||||||
|
sort: widget.sort,
|
||||||
|
filter: widget.filter,
|
||||||
|
);
|
||||||
|
|
||||||
if (!widget.pullToRefresh) {
|
if (!widget.pullToRefresh) {
|
||||||
return _buildListView(channelsBloc);
|
return child;
|
||||||
|
} else {
|
||||||
|
return RefreshIndicator(
|
||||||
|
onRefresh: () async {
|
||||||
|
_channelListController.loadData();
|
||||||
|
},
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildListView(
|
||||||
|
List<Channel> channels,
|
||||||
|
) {
|
||||||
|
var child;
|
||||||
|
|
||||||
|
if (channels.isNotEmpty) {
|
||||||
|
if (widget.crossAxisCount > 1) {
|
||||||
|
child = GridView.builder(
|
||||||
|
padding: widget.padding,
|
||||||
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
|
crossAxisCount: widget.crossAxisCount),
|
||||||
|
itemCount: channels.length,
|
||||||
|
physics: AlwaysScrollableScrollPhysics(),
|
||||||
|
controller: _scrollController,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
return _gridItemBuilder(context, index, channels);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
child = ListView.separated(
|
||||||
|
padding: widget.padding,
|
||||||
|
physics: AlwaysScrollableScrollPhysics(),
|
||||||
|
itemCount:
|
||||||
|
channels.isNotEmpty ? channels.length + 1 : channels.length,
|
||||||
|
separatorBuilder: (_, index) {
|
||||||
|
if (widget.separatorBuilder != null) {
|
||||||
|
return widget.separatorBuilder(context, index);
|
||||||
|
}
|
||||||
|
return _separatorBuilder(context, index);
|
||||||
|
},
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
return _listItemBuilder(context, index, channels);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return RefreshIndicator(
|
return AnimatedSwitcher(
|
||||||
onRefresh: () async {
|
child: child,
|
||||||
return channelsBloc.queryChannels(
|
duration: Duration(milliseconds: 500),
|
||||||
filter: widget.filter,
|
|
||||||
sortOptions: widget.sort,
|
|
||||||
paginationParams: widget.pagination,
|
|
||||||
options: widget.options,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: _buildListView(channelsBloc),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
StreamBuilder<List<Channel>> _buildListView(
|
Widget _buildEmptyWidget() {
|
||||||
ChannelsBlocState channelsBlocState,
|
if (widget.emptyBuilder != null) {
|
||||||
) {
|
return widget.emptyBuilder(context);
|
||||||
return StreamBuilder<List<Channel>>(
|
}
|
||||||
stream: channelsBlocState.channelsStream,
|
|
||||||
builder: (context, snapshot) {
|
|
||||||
var child;
|
|
||||||
if (snapshot.hasError) {
|
|
||||||
child = _buildErrorWidget(
|
|
||||||
snapshot,
|
|
||||||
context,
|
|
||||||
channelsBlocState,
|
|
||||||
);
|
|
||||||
} else if (!snapshot.hasData) {
|
|
||||||
child = _buildLoadingWidget();
|
|
||||||
} else {
|
|
||||||
final channels = snapshot.data;
|
|
||||||
|
|
||||||
if (channels.isEmpty && widget.emptyBuilder != null) {
|
return LayoutBuilder(
|
||||||
child = widget.emptyBuilder(context);
|
builder: (context, viewportConstraints) {
|
||||||
}
|
return SingleChildScrollView(
|
||||||
|
physics: AlwaysScrollableScrollPhysics(),
|
||||||
if (channels.isEmpty && widget.emptyBuilder == null) {
|
child: Stack(
|
||||||
child = LayoutBuilder(
|
children: [
|
||||||
builder: (context, viewportConstraints) {
|
ConstrainedBox(
|
||||||
return SingleChildScrollView(
|
constraints: BoxConstraints(
|
||||||
physics: AlwaysScrollableScrollPhysics(),
|
minHeight: viewportConstraints.maxHeight,
|
||||||
child: Stack(
|
),
|
||||||
children: [
|
child: Column(
|
||||||
ConstrainedBox(
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
constraints: BoxConstraints(
|
children: [
|
||||||
minHeight: viewportConstraints.maxHeight,
|
Padding(
|
||||||
),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Column(
|
child: StreamSvgIcon.message(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
size: 136,
|
||||||
children: [
|
color: StreamChatTheme.of(context)
|
||||||
Padding(
|
.colorTheme
|
||||||
padding: const EdgeInsets.all(8.0),
|
.greyGainsboro,
|
||||||
child: StreamSvgIcon.message(
|
|
||||||
size: 136,
|
|
||||||
color: StreamChatTheme.of(context)
|
|
||||||
.colorTheme
|
|
||||||
.greyGainsboro,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.all(8.0),
|
|
||||||
child: Text(
|
|
||||||
'Let’s start chatting!',
|
|
||||||
style: StreamChatTheme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.headline,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
vertical: 8.0,
|
|
||||||
horizontal: 52,
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
'How about sending your first message to a friend?',
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: StreamChatTheme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.body
|
|
||||||
.copyWith(
|
|
||||||
color: StreamChatTheme.of(context)
|
|
||||||
.colorTheme
|
|
||||||
.grey,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
if (widget.onStartChatPressed != null)
|
),
|
||||||
Positioned(
|
Padding(
|
||||||
right: 0,
|
padding: const EdgeInsets.all(8.0),
|
||||||
left: 0,
|
child: Text(
|
||||||
bottom: 32,
|
'Let’s start chatting!',
|
||||||
child: Center(
|
style: StreamChatTheme.of(context).textTheme.headline,
|
||||||
child: FlatButton(
|
),
|
||||||
onPressed: widget.onStartChatPressed,
|
),
|
||||||
child: Text(
|
Padding(
|
||||||
'Start a chat',
|
padding: const EdgeInsets.symmetric(
|
||||||
style: StreamChatTheme.of(context)
|
vertical: 8.0,
|
||||||
.textTheme
|
horizontal: 52,
|
||||||
.bodyBold
|
),
|
||||||
.copyWith(
|
child: Text(
|
||||||
color: StreamChatTheme.of(context)
|
'How about sending your first message to a friend?',
|
||||||
.colorTheme
|
textAlign: TextAlign.center,
|
||||||
.accentBlue,
|
style: StreamChatTheme.of(context)
|
||||||
),
|
.textTheme
|
||||||
),
|
.body
|
||||||
|
.copyWith(
|
||||||
|
color:
|
||||||
|
StreamChatTheme.of(context).colorTheme.grey,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (widget.onStartChatPressed != null)
|
||||||
|
Positioned(
|
||||||
|
right: 0,
|
||||||
|
left: 0,
|
||||||
|
bottom: 32,
|
||||||
|
child: Center(
|
||||||
|
child: FlatButton(
|
||||||
|
onPressed: widget.onStartChatPressed,
|
||||||
|
child: Text(
|
||||||
|
'Start a chat',
|
||||||
|
style: StreamChatTheme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodyBold
|
||||||
|
.copyWith(
|
||||||
|
color: StreamChatTheme.of(context)
|
||||||
|
.colorTheme
|
||||||
|
.accentBlue,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
},
|
],
|
||||||
);
|
),
|
||||||
}
|
|
||||||
|
|
||||||
if (channels.isNotEmpty) {
|
|
||||||
if (widget.crossAxisCount > 1) {
|
|
||||||
child = GridView.builder(
|
|
||||||
padding: widget.padding,
|
|
||||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
|
||||||
crossAxisCount: widget.crossAxisCount),
|
|
||||||
itemCount: channels.length,
|
|
||||||
physics: AlwaysScrollableScrollPhysics(),
|
|
||||||
controller: _scrollController,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
return _gridItemBuilder(context, index, channels);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
child = ListView.separated(
|
|
||||||
padding: widget.padding,
|
|
||||||
physics: AlwaysScrollableScrollPhysics(),
|
|
||||||
itemCount:
|
|
||||||
channels.isNotEmpty ? channels.length + 1 : channels.length,
|
|
||||||
separatorBuilder: (_, index) {
|
|
||||||
if (widget.separatorBuilder != null) {
|
|
||||||
return widget.separatorBuilder(context, index);
|
|
||||||
}
|
|
||||||
return _separatorBuilder(context, index);
|
|
||||||
},
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
return _listItemBuilder(context, index, channels);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return AnimatedSwitcher(
|
|
||||||
child: child,
|
|
||||||
duration: Duration(milliseconds: 500),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -443,27 +435,8 @@ class _ChannelListViewState extends State<ChannelListView>
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildErrorWidget(
|
Widget _buildErrorWidget(
|
||||||
AsyncSnapshot<List<Channel>> snapshot,
|
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
ChannelsBlocState channelsBlocState,
|
|
||||||
) {
|
) {
|
||||||
if (snapshot.error is Error) {
|
|
||||||
print((snapshot.error as Error).stackTrace);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (widget.errorBuilder != null) {
|
|
||||||
return widget.errorBuilder(snapshot.error);
|
|
||||||
}
|
|
||||||
|
|
||||||
var message = snapshot.error.toString();
|
|
||||||
if (snapshot.error is DioError) {
|
|
||||||
final dioError = snapshot.error as DioError;
|
|
||||||
if (dioError.type == DioErrorType.RESPONSE) {
|
|
||||||
message = dioError.message;
|
|
||||||
} else {
|
|
||||||
message = 'Check your connection and retry';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Center(
|
return Center(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
@@ -484,20 +457,9 @@ class _ChannelListViewState extends State<ChannelListView>
|
|||||||
),
|
),
|
||||||
style: Theme.of(context).textTheme.headline6,
|
style: Theme.of(context).textTheme.headline6,
|
||||||
),
|
),
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
top: 16.0,
|
|
||||||
),
|
|
||||||
child: Text(message),
|
|
||||||
),
|
|
||||||
FlatButton(
|
FlatButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
channelsBlocState.queryChannels(
|
_channelListController.loadData();
|
||||||
filter: widget.filter,
|
|
||||||
sortOptions: widget.sort,
|
|
||||||
paginationParams: widget.pagination,
|
|
||||||
options: widget.options,
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
child: Text('Retry'),
|
child: Text('Retry'),
|
||||||
),
|
),
|
||||||
@@ -714,14 +676,7 @@ class _ChannelListViewState extends State<ChannelListView>
|
|||||||
if (_scrollController.position.maxScrollExtent ==
|
if (_scrollController.position.maxScrollExtent ==
|
||||||
_scrollController.offset &&
|
_scrollController.offset &&
|
||||||
_scrollController.offset != 0) {
|
_scrollController.offset != 0) {
|
||||||
channelsProvider.queryChannels(
|
_channelListController.paginateData();
|
||||||
filter: widget.filter,
|
|
||||||
sortOptions: widget.sort,
|
|
||||||
paginationParams: widget.pagination.copyWith(
|
|
||||||
offset: channelsProvider.channels?.length ?? 0,
|
|
||||||
),
|
|
||||||
options: widget.options,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -759,12 +714,7 @@ class _ChannelListViewState extends State<ChannelListView>
|
|||||||
EventType.channelVisible,
|
EventType.channelVisible,
|
||||||
)
|
)
|
||||||
.listen((event) {
|
.listen((event) {
|
||||||
channelsBloc.queryChannels(
|
_channelListController.loadData();
|
||||||
filter: widget.filter,
|
|
||||||
sortOptions: widget.sort,
|
|
||||||
paginationParams: widget.pagination,
|
|
||||||
options: widget.options,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -777,13 +727,7 @@ class _ChannelListViewState extends State<ChannelListView>
|
|||||||
widget.pagination?.toJson()?.toString() !=
|
widget.pagination?.toJson()?.toString() !=
|
||||||
oldWidget.pagination?.toJson()?.toString() ||
|
oldWidget.pagination?.toJson()?.toString() ||
|
||||||
widget.options?.toString() != oldWidget.options?.toString()) {
|
widget.options?.toString() != oldWidget.options?.toString()) {
|
||||||
final channelsBloc = ChannelsBloc.of(context);
|
_channelListController.loadData();
|
||||||
channelsBloc.queryChannels(
|
|
||||||
filter: widget.filter,
|
|
||||||
sortOptions: widget.sort,
|
|
||||||
paginationParams: widget.pagination,
|
|
||||||
options: widget.options,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:stream_chat_flutter/src/lazy_load_scroll_view.dart';
|
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
import 'package:video_player/video_player.dart';
|
import 'package:video_player/video_player.dart';
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
import 'package:stream_chat/stream_chat.dart';
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
|
|
||||||
import '../stream_chat_flutter.dart';
|
import '../stream_chat_flutter.dart';
|
||||||
import 'stream_channel.dart';
|
|
||||||
|
|
||||||
/// It shows the current [Channel] name using a [Text] widget.
|
/// It shows the current [Channel] name using a [Text] widget.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:jiffy/jiffy.dart';
|
import 'package:jiffy/jiffy.dart';
|
||||||
import 'package:stream_chat/stream_chat.dart';
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
||||||
|
|
||||||
import '../stream_chat_flutter.dart';
|
import '../stream_chat_flutter.dart';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:stream_chat/stream_chat.dart';
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
|
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
|
||||||
|
|
||||||
class ChannelUnreadIndicator extends StatelessWidget {
|
class ChannelUnreadIndicator extends StatelessWidget {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import 'dart:io';
|
|||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:file_picker/file_picker.dart';
|
import 'package:file_picker/file_picker.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:stream_chat/stream_chat.dart';
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
|
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
|
||||||
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
||||||
import 'package:stream_chat_flutter/src/utils.dart';
|
import 'package:stream_chat_flutter/src/utils.dart';
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import 'package:photo_view/photo_view.dart';
|
|||||||
import 'package:stream_chat_flutter/src/image_footer.dart';
|
import 'package:stream_chat_flutter/src/image_footer.dart';
|
||||||
import 'package:stream_chat_flutter/src/image_header.dart';
|
import 'package:stream_chat_flutter/src/image_header.dart';
|
||||||
import 'package:video_player/video_player.dart';
|
import 'package:video_player/video_player.dart';
|
||||||
import 'stream_channel.dart';
|
|
||||||
|
|
||||||
import '../stream_chat_flutter.dart';
|
import '../stream_chat_flutter.dart';
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import 'package:dio/dio.dart';
|
|||||||
import 'package:esys_flutter_share/esys_flutter_share.dart';
|
import 'package:esys_flutter_share/esys_flutter_share.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:stream_chat/stream_chat.dart';
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
|
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:stream_chat/stream_chat.dart';
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
import 'package:stream_chat_flutter/src/full_screen_media.dart';
|
import 'package:stream_chat_flutter/src/full_screen_media.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:stream_chat/stream_chat.dart';
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
|
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
|
||||||
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
||||||
import 'image_actions_modal.dart';
|
import 'image_actions_modal.dart';
|
||||||
import 'stream_channel.dart';
|
|
||||||
|
|
||||||
class ImageHeader extends StatelessWidget implements PreferredSizeWidget {
|
class ImageHeader extends StatelessWidget implements PreferredSizeWidget {
|
||||||
/// True if this header shows the leading back button
|
/// True if this header shows the leading back button
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import 'package:flutter/foundation.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:photo_manager/photo_manager.dart';
|
import 'package:photo_manager/photo_manager.dart';
|
||||||
import 'package:stream_chat_flutter/src/lazy_load_scroll_view.dart';
|
|
||||||
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
||||||
|
|
||||||
import '../stream_chat_flutter.dart';
|
import '../stream_chat_flutter.dart';
|
||||||
|
|||||||
@@ -4,9 +4,8 @@ import 'dart:ui';
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:stream_chat/stream_chat.dart';
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
import 'package:stream_chat_flutter/src/reaction_picker.dart';
|
import 'package:stream_chat_flutter/src/reaction_picker.dart';
|
||||||
import 'package:stream_chat_flutter/src/stream_channel.dart';
|
|
||||||
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
||||||
import 'package:stream_chat_flutter/src/utils.dart';
|
import 'package:stream_chat_flutter/src/utils.dart';
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import 'package:http_parser/http_parser.dart' as httpParser;
|
|||||||
import 'package:image_picker/image_picker.dart';
|
import 'package:image_picker/image_picker.dart';
|
||||||
import 'package:mime/mime.dart';
|
import 'package:mime/mime.dart';
|
||||||
import 'package:photo_manager/photo_manager.dart';
|
import 'package:photo_manager/photo_manager.dart';
|
||||||
import 'package:stream_chat/stream_chat.dart';
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
import 'package:stream_chat_flutter/src/compress_video_service.dart';
|
import 'package:stream_chat_flutter/src/compress_video_service.dart';
|
||||||
import 'package:stream_chat_flutter/src/media_list_view.dart';
|
import 'package:stream_chat_flutter/src/media_list_view.dart';
|
||||||
import 'package:stream_chat_flutter/src/message_list_view.dart';
|
import 'package:stream_chat_flutter/src/message_list_view.dart';
|
||||||
@@ -26,7 +26,6 @@ import 'package:video_compress/video_compress.dart';
|
|||||||
import '../stream_chat_flutter.dart';
|
import '../stream_chat_flutter.dart';
|
||||||
import 'extension.dart';
|
import 'extension.dart';
|
||||||
import 'quoted_message_widget.dart';
|
import 'quoted_message_widget.dart';
|
||||||
import 'stream_channel.dart';
|
|
||||||
|
|
||||||
typedef FileUploader = Future<String> Function(PlatformFile, Channel);
|
typedef FileUploader = Future<String> Function(PlatformFile, Channel);
|
||||||
typedef AttachmentThumbnailBuilder = Widget Function(
|
typedef AttachmentThumbnailBuilder = Widget Function(
|
||||||
|
|||||||
@@ -6,9 +6,8 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:jiffy/jiffy.dart';
|
import 'package:jiffy/jiffy.dart';
|
||||||
import 'package:rxdart/rxdart.dart';
|
import 'package:rxdart/rxdart.dart';
|
||||||
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
||||||
import 'package:stream_chat/stream_chat.dart';
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
import 'package:stream_chat_flutter/src/info_tile.dart';
|
import 'package:stream_chat_flutter/src/info_tile.dart';
|
||||||
import 'package:stream_chat_flutter/src/lazy_load_scroll_view.dart';
|
|
||||||
import 'package:stream_chat_flutter/src/message_widget.dart';
|
import 'package:stream_chat_flutter/src/message_widget.dart';
|
||||||
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
||||||
import 'package:stream_chat_flutter/src/system_message.dart';
|
import 'package:stream_chat_flutter/src/system_message.dart';
|
||||||
@@ -16,7 +15,6 @@ import 'package:visibility_detector/visibility_detector.dart';
|
|||||||
|
|
||||||
import '../stream_chat_flutter.dart';
|
import '../stream_chat_flutter.dart';
|
||||||
import 'date_divider.dart';
|
import 'date_divider.dart';
|
||||||
import 'stream_channel.dart';
|
|
||||||
import 'swipeable.dart';
|
import 'swipeable.dart';
|
||||||
import 'extension.dart';
|
import 'extension.dart';
|
||||||
|
|
||||||
@@ -238,309 +236,300 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
|
|
||||||
bool _inBetweenList = false;
|
bool _inBetweenList = false;
|
||||||
|
|
||||||
|
MessageListController _messageListController = MessageListController();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final messagesStream = _isThreadConversation
|
return MessageListCore(
|
||||||
? streamChannel.channel.state.threadsStream
|
loadingBuilder: (context) {
|
||||||
.where((threads) => threads.containsKey(widget.parentMessage.id))
|
return Center(
|
||||||
.map((threads) => threads[widget.parentMessage.id])
|
child: const CircularProgressIndicator(),
|
||||||
: streamChannel.channel.state?.messagesStream;
|
);
|
||||||
|
},
|
||||||
|
emptyBuilder: (context) {
|
||||||
|
return Center(
|
||||||
|
child: Text(
|
||||||
|
'No chats here yet...',
|
||||||
|
style: StreamChatTheme.of(context).textTheme.footnote.copyWith(
|
||||||
|
color: StreamChatTheme.of(context)
|
||||||
|
.colorTheme
|
||||||
|
.black
|
||||||
|
.withOpacity(.5)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
messageListBuilder: (context, list) {
|
||||||
|
return _buildListView(list);
|
||||||
|
},
|
||||||
|
messageListController: _messageListController,
|
||||||
|
parentMessage: widget.parentMessage,
|
||||||
|
showScrollToBottom: widget.showScrollToBottom,
|
||||||
|
errorWidgetBuilder: (BuildContext context, Object error) {
|
||||||
|
return Center(
|
||||||
|
child: Text(
|
||||||
|
'Something went wrong',
|
||||||
|
style: StreamChatTheme.of(context).textTheme.footnote.copyWith(
|
||||||
|
color: StreamChatTheme.of(context)
|
||||||
|
.colorTheme
|
||||||
|
.black
|
||||||
|
.withOpacity(.5)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return StreamBuilder<List<Message>>(
|
Widget _buildListView(List<Message> data) {
|
||||||
stream: messagesStream?.map((messages) => messages
|
messages = data;
|
||||||
?.where((e) =>
|
final newMessagesListLength = messages.length;
|
||||||
(!e.isDeleted && e.shadowed != true) ||
|
|
||||||
(e.isDeleted &&
|
if (_messageListLength != null) {
|
||||||
e.user.id == streamChannel.channel.client.state.user.id))
|
if (_bottomPaginationActive || (_inBetweenList && _upToDate)) {
|
||||||
?.toList()),
|
if (_itemPositionListener.itemPositions.value?.isNotEmpty == true) {
|
||||||
builder: (context, snapshot) {
|
final first = _itemPositionListener.itemPositions.value.first;
|
||||||
if (!snapshot.hasData) {
|
final diff = newMessagesListLength - _messageListLength;
|
||||||
return Center(
|
if (diff > 0) {
|
||||||
child: const CircularProgressIndicator(),
|
initialIndex = first.index + diff;
|
||||||
);
|
initialAlignment = first.itemLeadingEdge;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
} else if (!_topPaginationActive && _upToDate) {
|
||||||
|
// Reset the index in-case we send any new message
|
||||||
|
initialIndex = 0;
|
||||||
|
initialAlignment = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final messageList = snapshot.data?.reversed?.toList() ?? [];
|
_messageListLength = newMessagesListLength;
|
||||||
if (messageList.isEmpty) {
|
final _client = StreamChat.of(context).client;
|
||||||
if (_upToDate) {
|
|
||||||
return Center(
|
return Stack(
|
||||||
child: Text(
|
alignment: Alignment.center,
|
||||||
'No chats here yet...',
|
children: [
|
||||||
style: StreamChatTheme.of(context)
|
ValueListenableBuilder<ConnectionStatus>(
|
||||||
.textTheme
|
valueListenable: _client.wsConnectionStatus,
|
||||||
.footnote
|
builder: (context, status, _) {
|
||||||
.copyWith(
|
String statusString = '';
|
||||||
color: StreamChatTheme.of(context)
|
bool showStatus = true;
|
||||||
.colorTheme
|
|
||||||
.black
|
switch (status) {
|
||||||
.withOpacity(.5)),
|
case ConnectionStatus.connected:
|
||||||
|
statusString = 'Connected';
|
||||||
|
showStatus = false;
|
||||||
|
break;
|
||||||
|
case ConnectionStatus.connecting:
|
||||||
|
statusString = 'Reconnecting...';
|
||||||
|
break;
|
||||||
|
case ConnectionStatus.disconnected:
|
||||||
|
statusString = 'Disconnected';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return InfoTile(
|
||||||
|
showMessage:
|
||||||
|
widget.showConnectionStateTile ? showStatus : false,
|
||||||
|
tileAnchor: Alignment.topCenter,
|
||||||
|
childAnchor: Alignment.topCenter,
|
||||||
|
message: statusString,
|
||||||
|
child: LazyLoadScrollView(
|
||||||
|
onStartOfPage: () async {
|
||||||
|
_inBetweenList = false;
|
||||||
|
if (!_upToDate) {
|
||||||
|
_topPaginationActive = false;
|
||||||
|
_bottomPaginationActive = true;
|
||||||
|
return _paginateData(
|
||||||
|
streamChannel,
|
||||||
|
QueryDirection.bottom,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onEndOfPage: () async {
|
||||||
|
_inBetweenList = false;
|
||||||
|
_topPaginationActive = true;
|
||||||
|
_bottomPaginationActive = false;
|
||||||
|
return _paginateData(
|
||||||
|
streamChannel,
|
||||||
|
QueryDirection.top,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onInBetweenOfPage: () {
|
||||||
|
_inBetweenList = true;
|
||||||
|
},
|
||||||
|
child: ScrollablePositionedList.separated(
|
||||||
|
key: ValueKey(initialIndex + initialAlignment),
|
||||||
|
itemPositionsListener: _itemPositionListener,
|
||||||
|
addAutomaticKeepAlives: true,
|
||||||
|
initialScrollIndex: initialIndex ?? 0,
|
||||||
|
initialAlignment: initialAlignment ?? 0,
|
||||||
|
physics: widget.scrollPhysics,
|
||||||
|
itemScrollController: _scrollController,
|
||||||
|
reverse: true,
|
||||||
|
itemCount:
|
||||||
|
messages.length + 2 + (_isThreadConversation ? 1 : 0),
|
||||||
|
separatorBuilder: (context, i) {
|
||||||
|
if (i == messages.length) return Offstage();
|
||||||
|
if (i == 0) return SizedBox(height: 30);
|
||||||
|
if (i == messages.length + 1) {
|
||||||
|
final replyCount = widget.parentMessage.replyCount;
|
||||||
|
return Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
gradient: StreamChatTheme.of(context)
|
||||||
|
.colorTheme
|
||||||
|
.bgGradient,
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Text(
|
||||||
|
'$replyCount ${replyCount == 1 ? 'Reply' : 'Replies'}',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: StreamChatTheme.of(context)
|
||||||
|
.channelTheme
|
||||||
|
.channelHeaderTheme
|
||||||
|
.lastMessageAt,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final message = messages[i];
|
||||||
|
final nextMessage = messages[i - 1];
|
||||||
|
if (!Jiffy(message.createdAt.toLocal()).isSame(
|
||||||
|
nextMessage.createdAt.toLocal(),
|
||||||
|
Units.DAY,
|
||||||
|
)) {
|
||||||
|
final divider = widget.dateDividerBuilder != null
|
||||||
|
? widget.dateDividerBuilder(
|
||||||
|
nextMessage.createdAt.toLocal(),
|
||||||
|
)
|
||||||
|
: DateDivider(
|
||||||
|
dateTime: nextMessage.createdAt.toLocal(),
|
||||||
|
);
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 12.0),
|
||||||
|
child: divider,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
final timeDiff =
|
||||||
|
Jiffy(nextMessage.createdAt.toLocal()).diff(
|
||||||
|
message.createdAt.toLocal(),
|
||||||
|
Units.MINUTE,
|
||||||
|
);
|
||||||
|
|
||||||
|
final isNextUserSame =
|
||||||
|
message.user.id == nextMessage.user?.id;
|
||||||
|
final isThread = message.replyCount > 0;
|
||||||
|
final isDeleted = message.isDeleted;
|
||||||
|
if (timeDiff >= 1 ||
|
||||||
|
!isNextUserSame ||
|
||||||
|
isThread ||
|
||||||
|
isDeleted) {
|
||||||
|
return SizedBox(height: 8);
|
||||||
|
}
|
||||||
|
return SizedBox(height: 2);
|
||||||
|
},
|
||||||
|
itemBuilder: (context, i) {
|
||||||
|
if (i == messages.length + 2) {
|
||||||
|
if (widget.parentMessageBuilder != null) {
|
||||||
|
return widget.parentMessageBuilder(
|
||||||
|
context,
|
||||||
|
widget.parentMessage,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return buildParentMessage(widget.parentMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i == messages.length + 1) {
|
||||||
|
return _buildLoadingIndicator(
|
||||||
|
streamChannel,
|
||||||
|
QueryDirection.top,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (i == 0) {
|
||||||
|
return _buildLoadingIndicator(
|
||||||
|
streamChannel,
|
||||||
|
QueryDirection.bottom,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
final message = messages[i - 1];
|
||||||
|
|
||||||
|
Widget messageWidget;
|
||||||
|
|
||||||
|
if (i == 1) {
|
||||||
|
messageWidget = _buildBottomMessage(
|
||||||
|
context,
|
||||||
|
message,
|
||||||
|
messages,
|
||||||
|
streamChannel,
|
||||||
|
);
|
||||||
|
} else if (i == messages.length - 1) {
|
||||||
|
messageWidget = _buildTopMessage(
|
||||||
|
context,
|
||||||
|
message,
|
||||||
|
messages,
|
||||||
|
streamChannel,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
if (widget.messageBuilder != null) {
|
||||||
|
messageWidget = Builder(
|
||||||
|
key: ValueKey<String>('MESSAGE-${message.id}'),
|
||||||
|
builder: (context) => widget.messageBuilder(
|
||||||
|
context,
|
||||||
|
MessageDetails(
|
||||||
|
context,
|
||||||
|
message,
|
||||||
|
messages,
|
||||||
|
i,
|
||||||
|
),
|
||||||
|
messages),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
messageWidget = buildMessage(message, messages, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return messageWidget;
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}),
|
||||||
} else {
|
if (widget.showScrollToBottom) _buildScrollToBottom(),
|
||||||
messages = messageList;
|
Positioned(
|
||||||
}
|
top: 20.0,
|
||||||
|
child: ValueListenableBuilder<Iterable<ItemPosition>>(
|
||||||
final newMessagesListLength = messages.length;
|
valueListenable: _itemPositionListener.itemPositions,
|
||||||
|
builder: (context, values, _) {
|
||||||
if (_messageListLength != null) {
|
final items = _itemPositionListener.itemPositions?.value;
|
||||||
if (_bottomPaginationActive || (_inBetweenList && _upToDate)) {
|
if (items.isEmpty || messages.isEmpty) {
|
||||||
if (_itemPositionListener.itemPositions.value?.isNotEmpty ==
|
return SizedBox();
|
||||||
true) {
|
|
||||||
final first = _itemPositionListener.itemPositions.value.first;
|
|
||||||
final diff = newMessagesListLength - _messageListLength;
|
|
||||||
if (diff > 0) {
|
|
||||||
initialIndex = first.index + diff;
|
|
||||||
initialAlignment = first.itemLeadingEdge;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if (!_topPaginationActive && _upToDate) {
|
|
||||||
// Reset the index in-case we send any new message
|
|
||||||
initialIndex = 0;
|
|
||||||
initialAlignment = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_messageListLength = newMessagesListLength;
|
var index = _getTopElement(values).index;
|
||||||
final _client = StreamChat.of(context).client;
|
|
||||||
|
|
||||||
return Stack(
|
if (index > messages.length) {
|
||||||
alignment: Alignment.center,
|
return SizedBox();
|
||||||
children: [
|
}
|
||||||
ValueListenableBuilder<ConnectionStatus>(
|
|
||||||
valueListenable: _client.wsConnectionStatus,
|
|
||||||
builder: (context, status, _) {
|
|
||||||
String statusString = '';
|
|
||||||
bool showStatus = true;
|
|
||||||
|
|
||||||
switch (status) {
|
if (index == messages.length) {
|
||||||
case ConnectionStatus.connected:
|
index = max(index - 1, 0);
|
||||||
statusString = 'Connected';
|
}
|
||||||
showStatus = false;
|
|
||||||
break;
|
|
||||||
case ConnectionStatus.connecting:
|
|
||||||
statusString = 'Reconnecting...';
|
|
||||||
break;
|
|
||||||
case ConnectionStatus.disconnected:
|
|
||||||
statusString = 'Disconnected';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return InfoTile(
|
return widget.dateDividerBuilder != null
|
||||||
showMessage:
|
? widget.dateDividerBuilder(
|
||||||
widget.showConnectionStateTile ? showStatus : false,
|
messages[index].createdAt.toLocal(),
|
||||||
tileAnchor: Alignment.topCenter,
|
)
|
||||||
childAnchor: Alignment.topCenter,
|
: DateDivider(
|
||||||
message: statusString,
|
dateTime: messages[index].createdAt.toLocal(),
|
||||||
child: LazyLoadScrollView(
|
|
||||||
onStartOfPage: () async {
|
|
||||||
_inBetweenList = false;
|
|
||||||
if (!_upToDate) {
|
|
||||||
_topPaginationActive = false;
|
|
||||||
_bottomPaginationActive = true;
|
|
||||||
return _paginateData(
|
|
||||||
streamChannel,
|
|
||||||
QueryDirection.bottom,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onEndOfPage: () async {
|
|
||||||
_inBetweenList = false;
|
|
||||||
_topPaginationActive = true;
|
|
||||||
_bottomPaginationActive = false;
|
|
||||||
return _paginateData(
|
|
||||||
streamChannel,
|
|
||||||
QueryDirection.top,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
onInBetweenOfPage: () {
|
|
||||||
_inBetweenList = true;
|
|
||||||
},
|
|
||||||
child: ScrollablePositionedList.separated(
|
|
||||||
key: ValueKey(initialIndex + initialAlignment),
|
|
||||||
itemPositionsListener: _itemPositionListener,
|
|
||||||
addAutomaticKeepAlives: true,
|
|
||||||
initialScrollIndex: initialIndex ?? 0,
|
|
||||||
initialAlignment: initialAlignment ?? 0,
|
|
||||||
physics: widget.scrollPhysics,
|
|
||||||
itemScrollController: _scrollController,
|
|
||||||
reverse: true,
|
|
||||||
itemCount: messages.length +
|
|
||||||
2 +
|
|
||||||
(_isThreadConversation ? 1 : 0),
|
|
||||||
separatorBuilder: (context, i) {
|
|
||||||
if (i == messages.length) return Offstage();
|
|
||||||
if (i == 0) return SizedBox(height: 30);
|
|
||||||
if (i == messages.length + 1) {
|
|
||||||
final replyCount =
|
|
||||||
widget.parentMessage.replyCount;
|
|
||||||
return Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
gradient: StreamChatTheme.of(context)
|
|
||||||
.colorTheme
|
|
||||||
.bgGradient,
|
|
||||||
),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(8.0),
|
|
||||||
child: Text(
|
|
||||||
'$replyCount ${replyCount == 1 ? 'Reply' : 'Replies'}',
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: StreamChatTheme.of(context)
|
|
||||||
.channelTheme
|
|
||||||
.channelHeaderTheme
|
|
||||||
.lastMessageAt,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final message = messages[i];
|
|
||||||
final nextMessage = messages[i - 1];
|
|
||||||
if (!Jiffy(message.createdAt.toLocal()).isSame(
|
|
||||||
nextMessage.createdAt.toLocal(),
|
|
||||||
Units.DAY,
|
|
||||||
)) {
|
|
||||||
final divider = widget.dateDividerBuilder != null
|
|
||||||
? widget.dateDividerBuilder(
|
|
||||||
nextMessage.createdAt.toLocal(),
|
|
||||||
)
|
|
||||||
: DateDivider(
|
|
||||||
dateTime: nextMessage.createdAt.toLocal(),
|
|
||||||
);
|
|
||||||
return Padding(
|
|
||||||
padding:
|
|
||||||
const EdgeInsets.symmetric(vertical: 12.0),
|
|
||||||
child: divider,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
final timeDiff =
|
|
||||||
Jiffy(nextMessage.createdAt.toLocal()).diff(
|
|
||||||
message.createdAt.toLocal(),
|
|
||||||
Units.MINUTE,
|
|
||||||
);
|
|
||||||
|
|
||||||
final isNextUserSame =
|
|
||||||
message.user.id == nextMessage.user?.id;
|
|
||||||
final isThread = message.replyCount > 0;
|
|
||||||
final isDeleted = message.isDeleted;
|
|
||||||
if (timeDiff >= 1 ||
|
|
||||||
!isNextUserSame ||
|
|
||||||
isThread ||
|
|
||||||
isDeleted) {
|
|
||||||
return SizedBox(height: 8);
|
|
||||||
}
|
|
||||||
return SizedBox(height: 2);
|
|
||||||
},
|
|
||||||
itemBuilder: (context, i) {
|
|
||||||
if (i == messages.length + 2) {
|
|
||||||
if (widget.parentMessageBuilder != null) {
|
|
||||||
return widget.parentMessageBuilder(
|
|
||||||
context,
|
|
||||||
widget.parentMessage,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return buildParentMessage(widget.parentMessage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (i == messages.length + 1) {
|
|
||||||
return _buildLoadingIndicator(
|
|
||||||
streamChannel,
|
|
||||||
QueryDirection.top,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (i == 0) {
|
|
||||||
return _buildLoadingIndicator(
|
|
||||||
streamChannel,
|
|
||||||
QueryDirection.bottom,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
final message = messages[i - 1];
|
|
||||||
|
|
||||||
Widget messageWidget;
|
|
||||||
|
|
||||||
if (i == 1) {
|
|
||||||
messageWidget = _buildBottomMessage(
|
|
||||||
context,
|
|
||||||
message,
|
|
||||||
messages,
|
|
||||||
streamChannel,
|
|
||||||
);
|
|
||||||
} else if (i == messages.length - 1) {
|
|
||||||
messageWidget = _buildTopMessage(
|
|
||||||
context,
|
|
||||||
message,
|
|
||||||
messages,
|
|
||||||
streamChannel,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
if (widget.messageBuilder != null) {
|
|
||||||
messageWidget = Builder(
|
|
||||||
key:
|
|
||||||
ValueKey<String>('MESSAGE-${message.id}'),
|
|
||||||
builder: (context) => widget.messageBuilder(
|
|
||||||
context,
|
|
||||||
MessageDetails(
|
|
||||||
context,
|
|
||||||
message,
|
|
||||||
messages,
|
|
||||||
i,
|
|
||||||
),
|
|
||||||
messages),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
messageWidget =
|
|
||||||
buildMessage(message, messages, i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return messageWidget;
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}),
|
},
|
||||||
if (widget.showScrollToBottom) _buildScrollToBottom(),
|
),
|
||||||
Positioned(
|
),
|
||||||
top: 20.0,
|
],
|
||||||
child: ValueListenableBuilder<Iterable<ItemPosition>>(
|
);
|
||||||
valueListenable: _itemPositionListener.itemPositions,
|
|
||||||
builder: (context, values, _) {
|
|
||||||
final items = _itemPositionListener.itemPositions?.value;
|
|
||||||
if (items.isEmpty || messages.isEmpty) {
|
|
||||||
return SizedBox();
|
|
||||||
}
|
|
||||||
|
|
||||||
var index = _getTopElement(values).index;
|
|
||||||
|
|
||||||
if (index > messages.length) {
|
|
||||||
return SizedBox();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index == messages.length) {
|
|
||||||
index = max(index - 1, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return widget.dateDividerBuilder != null
|
|
||||||
? widget.dateDividerBuilder(
|
|
||||||
messages[index].createdAt.toLocal(),
|
|
||||||
)
|
|
||||||
: DateDivider(
|
|
||||||
dateTime: messages[index].createdAt.toLocal(),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _paginateData(
|
Future<void> _paginateData(
|
||||||
StreamChannelState channel, QueryDirection direction) {
|
StreamChannelState channel, QueryDirection direction) {
|
||||||
if (!_isThreadConversation) {
|
return _messageListController.paginateData(direction: direction);
|
||||||
return channel.queryMessages(direction: direction);
|
|
||||||
} else {
|
|
||||||
return channel.getReplies(widget.parentMessage.id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ItemPosition _getTopElement(Iterable<ItemPosition> values) {
|
ItemPosition _getTopElement(Iterable<ItemPosition> values) {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:stream_chat/stream_chat.dart';
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
import 'package:stream_chat_flutter/src/reaction_bubble.dart';
|
import 'package:stream_chat_flutter/src/reaction_bubble.dart';
|
||||||
import 'package:stream_chat_flutter/src/reaction_picker.dart';
|
import 'package:stream_chat_flutter/src/reaction_picker.dart';
|
||||||
import 'package:stream_chat_flutter/src/stream_chat.dart';
|
import 'package:stream_chat_flutter/src/stream_chat.dart';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:jiffy/jiffy.dart';
|
import 'package:jiffy/jiffy.dart';
|
||||||
import 'package:stream_chat/stream_chat.dart';
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
/// It shows the current [Message] preview.
|
/// It shows the current [Message] preview.
|
||||||
|
|||||||
@@ -1,13 +1,9 @@
|
|||||||
import 'dart:convert';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:stream_chat/stream_chat.dart';
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
import 'package:stream_chat_flutter/src/info_tile.dart';
|
import 'package:stream_chat_flutter/src/info_tile.dart';
|
||||||
import 'package:stream_chat_flutter/src/message_search_item.dart';
|
import 'package:stream_chat_flutter/src/message_search_item.dart';
|
||||||
|
|
||||||
import '../stream_chat_flutter.dart';
|
import '../stream_chat_flutter.dart';
|
||||||
import 'lazy_load_scroll_view.dart';
|
|
||||||
import 'message_search_bloc.dart';
|
|
||||||
|
|
||||||
/// Callback called when tapping on a user
|
/// Callback called when tapping on a user
|
||||||
typedef MessageSearchItemTapCallback = void Function(GetMessageResponse);
|
typedef MessageSearchItemTapCallback = void Function(GetMessageResponse);
|
||||||
@@ -120,23 +116,115 @@ class MessageSearchListView extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _MessageSearchListViewState extends State<MessageSearchListView> {
|
class _MessageSearchListViewState extends State<MessageSearchListView> {
|
||||||
@override
|
MessageSearchListController _messageSearchListController =
|
||||||
void initState() {
|
MessageSearchListController();
|
||||||
super.initState();
|
|
||||||
final messageSearchBloc = MessageSearchBloc.of(context);
|
|
||||||
messageSearchBloc.search(
|
|
||||||
filter: widget.filters,
|
|
||||||
sort: widget.sortOptions,
|
|
||||||
query: widget.messageQuery,
|
|
||||||
pagination: widget.paginationParams,
|
|
||||||
messageFilter: widget.messageFilters,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final messageSearchBloc = MessageSearchBloc.of(context);
|
return MessageSearchListCore(
|
||||||
return _buildListView(messageSearchBloc);
|
filters: widget.filters,
|
||||||
|
sortOptions: widget.sortOptions,
|
||||||
|
messageQuery: widget.messageQuery,
|
||||||
|
paginationParams: widget.paginationParams,
|
||||||
|
messageFilters: widget.messageFilters,
|
||||||
|
messageSearchListController: _messageSearchListController,
|
||||||
|
emptyBuilder: (context) {
|
||||||
|
if (widget.emptyBuilder != null) {
|
||||||
|
return widget.emptyBuilder(context, widget.messageQuery);
|
||||||
|
}
|
||||||
|
return LayoutBuilder(
|
||||||
|
builder: (context, viewportConstraints) {
|
||||||
|
return SingleChildScrollView(
|
||||||
|
physics: AlwaysScrollableScrollPhysics(),
|
||||||
|
child: ConstrainedBox(
|
||||||
|
constraints: BoxConstraints(
|
||||||
|
minHeight: viewportConstraints.maxHeight,
|
||||||
|
),
|
||||||
|
child: Center(
|
||||||
|
child: Text('There are no messages currently'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
errorBuilder: (error) {
|
||||||
|
if (error is Error) {
|
||||||
|
print((error).stackTrace);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (widget.errorBuilder != null) {
|
||||||
|
return widget.errorBuilder(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
var message = error.toString();
|
||||||
|
if (error is DioError) {
|
||||||
|
final dioError = error as DioError;
|
||||||
|
if (dioError.type == DioErrorType.RESPONSE) {
|
||||||
|
message = dioError.message;
|
||||||
|
} else {
|
||||||
|
message = 'Check your connection and retry';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return InfoTile(
|
||||||
|
showMessage: widget.showErrorTile,
|
||||||
|
tileAnchor: Alignment.topCenter,
|
||||||
|
childAnchor: Alignment.topCenter,
|
||||||
|
message: 'An error occurred.',
|
||||||
|
child: Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
Text.rich(
|
||||||
|
TextSpan(
|
||||||
|
children: [
|
||||||
|
WidgetSpan(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(right: 2.0),
|
||||||
|
child: Icon(Icons.error_outline),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextSpan(text: 'Error loading messages'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
style: Theme.of(context).textTheme.headline6,
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 16.0),
|
||||||
|
child: Text(message),
|
||||||
|
),
|
||||||
|
RaisedButton(
|
||||||
|
onPressed: () {
|
||||||
|
_messageSearchListController.loadData();
|
||||||
|
},
|
||||||
|
child: Text('Retry'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
loadingBuilder: (context) {
|
||||||
|
return LayoutBuilder(
|
||||||
|
builder: (context, viewportConstraints) {
|
||||||
|
return SingleChildScrollView(
|
||||||
|
physics: AlwaysScrollableScrollPhysics(),
|
||||||
|
child: ConstrainedBox(
|
||||||
|
constraints: BoxConstraints(
|
||||||
|
minHeight: viewportConstraints.maxHeight,
|
||||||
|
),
|
||||||
|
child: Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
childBuilder: (list) {
|
||||||
|
return _buildListView(list);
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _separatorBuilder(BuildContext context, int index) {
|
Widget _separatorBuilder(BuildContext context, int index) {
|
||||||
@@ -157,8 +245,9 @@ class _MessageSearchListViewState extends State<MessageSearchListView> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildQueryProgressIndicator(
|
Widget _buildQueryProgressIndicator(context) {
|
||||||
context, MessageSearchBlocState messageSearchBloc) {
|
MessageSearchBlocState messageSearchBloc = MessageSearchBloc.of(context);
|
||||||
|
|
||||||
return StreamBuilder<bool>(
|
return StreamBuilder<bool>(
|
||||||
stream: messageSearchBloc.queryMessagesLoading,
|
stream: messageSearchBloc.queryMessagesLoading,
|
||||||
initialData: false,
|
initialData: false,
|
||||||
@@ -187,204 +276,66 @@ class _MessageSearchListViewState extends State<MessageSearchListView> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildListView(MessageSearchBlocState messageSearchBloc) {
|
Widget _buildListView(List<GetMessageResponse> data) {
|
||||||
return StreamBuilder<List<GetMessageResponse>>(
|
final items = data;
|
||||||
stream: messageSearchBloc.messagesStream,
|
|
||||||
builder: (context, snapshot) {
|
|
||||||
if (snapshot.hasError) {
|
|
||||||
if (snapshot.error is Error) {
|
|
||||||
print((snapshot.error as Error).stackTrace);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (widget.errorBuilder != null) {
|
Widget child = ListView.separated(
|
||||||
return widget.errorBuilder(snapshot.error);
|
physics: AlwaysScrollableScrollPhysics(),
|
||||||
}
|
itemCount: items.isNotEmpty ? items.length + 1 : items.length,
|
||||||
|
separatorBuilder: (_, index) {
|
||||||
var message = snapshot.error.toString();
|
if (widget.separatorBuilder != null) {
|
||||||
if (snapshot.error is DioError) {
|
return widget.separatorBuilder(context, index);
|
||||||
final dioError = snapshot.error as DioError;
|
|
||||||
if (dioError.type == DioErrorType.RESPONSE) {
|
|
||||||
message = dioError.message;
|
|
||||||
} else {
|
|
||||||
message = 'Check your connection and retry';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return InfoTile(
|
|
||||||
showMessage: widget.showErrorTile,
|
|
||||||
tileAnchor: Alignment.topCenter,
|
|
||||||
childAnchor: Alignment.topCenter,
|
|
||||||
message: 'An error occurred.',
|
|
||||||
child: Center(
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: <Widget>[
|
|
||||||
Text.rich(
|
|
||||||
TextSpan(
|
|
||||||
children: [
|
|
||||||
WidgetSpan(
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.only(right: 2.0),
|
|
||||||
child: Icon(Icons.error_outline),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
TextSpan(text: 'Error loading messages'),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
style: Theme.of(context).textTheme.headline6,
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(top: 16.0),
|
|
||||||
child: Text(message),
|
|
||||||
),
|
|
||||||
RaisedButton(
|
|
||||||
onPressed: () {
|
|
||||||
messageSearchBloc.search(
|
|
||||||
filter: widget.filters,
|
|
||||||
sort: widget.sortOptions,
|
|
||||||
query: widget.messageQuery,
|
|
||||||
pagination: widget.paginationParams,
|
|
||||||
messageFilter: widget.messageFilters,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: Text('Retry'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
return _separatorBuilder(context, index);
|
||||||
if (!snapshot.hasData) {
|
},
|
||||||
return LayoutBuilder(
|
itemBuilder: (context, index) {
|
||||||
builder: (context, viewportConstraints) {
|
if (index < items.length) {
|
||||||
return SingleChildScrollView(
|
return _listItemBuilder(context, items[index]);
|
||||||
physics: AlwaysScrollableScrollPhysics(),
|
|
||||||
child: ConstrainedBox(
|
|
||||||
constraints: BoxConstraints(
|
|
||||||
minHeight: viewportConstraints.maxHeight,
|
|
||||||
),
|
|
||||||
child: Center(
|
|
||||||
child: CircularProgressIndicator(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
return _buildQueryProgressIndicator(context);
|
||||||
final items = snapshot.data;
|
|
||||||
|
|
||||||
if (items.isEmpty) {
|
|
||||||
if (widget.emptyBuilder != null) {
|
|
||||||
return widget.emptyBuilder(context, widget.messageQuery);
|
|
||||||
}
|
|
||||||
return LayoutBuilder(
|
|
||||||
builder: (context, viewportConstraints) {
|
|
||||||
return SingleChildScrollView(
|
|
||||||
physics: AlwaysScrollableScrollPhysics(),
|
|
||||||
child: ConstrainedBox(
|
|
||||||
constraints: BoxConstraints(
|
|
||||||
minHeight: viewportConstraints.maxHeight,
|
|
||||||
),
|
|
||||||
child: Center(
|
|
||||||
child: Text('There are no messages currently'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget child = ListView.separated(
|
|
||||||
physics: AlwaysScrollableScrollPhysics(),
|
|
||||||
itemCount: items.isNotEmpty ? items.length + 1 : items.length,
|
|
||||||
separatorBuilder: (_, index) {
|
|
||||||
if (widget.separatorBuilder != null) {
|
|
||||||
return widget.separatorBuilder(context, index);
|
|
||||||
}
|
|
||||||
return _separatorBuilder(context, index);
|
|
||||||
},
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
if (index < items.length) {
|
|
||||||
return _listItemBuilder(context, items[index]);
|
|
||||||
}
|
|
||||||
return _buildQueryProgressIndicator(context, messageSearchBloc);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
if (widget.pullToRefresh) {
|
|
||||||
child = RefreshIndicator(
|
|
||||||
onRefresh: () async => messageSearchBloc.search(
|
|
||||||
filter: widget.filters,
|
|
||||||
sort: widget.sortOptions,
|
|
||||||
query: widget.messageQuery,
|
|
||||||
pagination: widget.paginationParams,
|
|
||||||
messageFilter: widget.messageFilters,
|
|
||||||
),
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
child = LazyLoadScrollView(
|
|
||||||
onEndOfPage: () => messageSearchBloc.loadMore(
|
|
||||||
filter: widget.filters,
|
|
||||||
sort: widget.sortOptions,
|
|
||||||
pagination: widget.paginationParams.copyWith(
|
|
||||||
offset: messageSearchBloc.messageResponses?.length ?? 0,
|
|
||||||
),
|
|
||||||
query: widget.messageQuery,
|
|
||||||
messageFilter: widget.messageFilters,
|
|
||||||
),
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (widget.showResultCount) {
|
|
||||||
child = Column(
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
width: double.maxFinite,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
gradient: StreamChatTheme.of(context).colorTheme.bgGradient,
|
|
||||||
),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
vertical: 8,
|
|
||||||
horizontal: 8,
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
'${items.length} results',
|
|
||||||
style: TextStyle(
|
|
||||||
color: StreamChatTheme.of(context).colorTheme.grey,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Expanded(child: child),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return child;
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
if (widget.pullToRefresh) {
|
||||||
|
child = RefreshIndicator(
|
||||||
@override
|
onRefresh: () async {
|
||||||
void didUpdateWidget(MessageSearchListView oldWidget) {
|
_messageSearchListController.loadData();
|
||||||
super.didUpdateWidget(oldWidget);
|
},
|
||||||
if (widget.filters?.toString() != oldWidget.filters?.toString() ||
|
child: child,
|
||||||
jsonEncode(widget.sortOptions) != jsonEncode(oldWidget.sortOptions) ||
|
|
||||||
widget.paginationParams?.toJson()?.toString() !=
|
|
||||||
oldWidget.paginationParams?.toJson()?.toString() ||
|
|
||||||
widget.messageQuery?.toString() != oldWidget.messageQuery?.toString() ||
|
|
||||||
widget.messageFilters?.toString() !=
|
|
||||||
oldWidget.messageFilters?.toString()) {
|
|
||||||
final messageSearchBloc = MessageSearchBloc.of(context);
|
|
||||||
messageSearchBloc.search(
|
|
||||||
filter: widget.filters,
|
|
||||||
sort: widget.sortOptions,
|
|
||||||
query: widget.messageQuery,
|
|
||||||
pagination: widget.paginationParams,
|
|
||||||
messageFilter: widget.messageFilters,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
child = LazyLoadScrollView(
|
||||||
|
onEndOfPage: () async {
|
||||||
|
return _messageSearchListController.paginateData();
|
||||||
|
},
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (widget.showResultCount) {
|
||||||
|
child = Column(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
width: double.maxFinite,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
gradient: StreamChatTheme.of(context).colorTheme.bgGradient,
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: 8,
|
||||||
|
horizontal: 8,
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
'${items.length} results',
|
||||||
|
style: TextStyle(
|
||||||
|
color: StreamChatTheme.of(context).colorTheme.grey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(child: child),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return child;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_markdown/flutter_markdown.dart';
|
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||||
import 'package:stream_chat/stream_chat.dart';
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
|
|
||||||
import 'stream_chat_theme.dart';
|
import 'stream_chat_theme.dart';
|
||||||
import 'utils.dart';
|
import 'utils.dart';
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import 'dart:math';
|
|||||||
|
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:stream_chat/stream_chat.dart';
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
import 'package:video_player/video_player.dart';
|
import 'package:video_player/video_player.dart';
|
||||||
|
|
||||||
import 'attachment_error.dart';
|
import 'attachment_error.dart';
|
||||||
|
|||||||
@@ -4,8 +4,9 @@ import 'package:flutter/foundation.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_app_badger/flutter_app_badger.dart';
|
import 'package:flutter_app_badger/flutter_app_badger.dart';
|
||||||
import 'package:flutter_portal/flutter_portal.dart';
|
import 'package:flutter_portal/flutter_portal.dart';
|
||||||
import 'package:stream_chat/stream_chat.dart';
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
|
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
|
||||||
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
/// Widget used to provide information about the chat to the widget tree
|
/// Widget used to provide information about the chat to the widget tree
|
||||||
///
|
///
|
||||||
@@ -61,9 +62,8 @@ class StreamChat extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The current state of the StreamChat widget
|
/// The current state of the StreamChat widget
|
||||||
class StreamChatState extends State<StreamChat> with WidgetsBindingObserver {
|
class StreamChatState extends State<StreamChat> {
|
||||||
Client get client => widget.client;
|
Client get client => widget.client;
|
||||||
Timer _disconnectTimer;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -81,7 +81,10 @@ class StreamChatState extends State<StreamChat> with WidgetsBindingObserver {
|
|||||||
accentColor: streamTheme.colorTheme.accentBlue,
|
accentColor: streamTheme.colorTheme.accentBlue,
|
||||||
scaffoldBackgroundColor: streamTheme.colorTheme.white,
|
scaffoldBackgroundColor: streamTheme.colorTheme.white,
|
||||||
),
|
),
|
||||||
child: widget.child,
|
child: StreamChatCore(
|
||||||
|
child: widget.child,
|
||||||
|
client: client,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -106,7 +109,6 @@ class StreamChatState extends State<StreamChat> with WidgetsBindingObserver {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
WidgetsBinding.instance.addObserver(this);
|
|
||||||
client.state?.totalUnreadCountStream?.listen((count) {
|
client.state?.totalUnreadCountStream?.listen((count) {
|
||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
FlutterAppBadger.updateBadgeCount(count);
|
FlutterAppBadger.updateBadgeCount(count);
|
||||||
@@ -115,67 +117,4 @@ class StreamChatState extends State<StreamChat> with WidgetsBindingObserver {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
StreamSubscription _newMessageSubscription;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
|
||||||
if (client.state?.user != null) {
|
|
||||||
if (state == AppLifecycleState.paused) {
|
|
||||||
if (client.showLocalNotification != null) {
|
|
||||||
_newMessageSubscription = client
|
|
||||||
.on(EventType.messageNew)
|
|
||||||
.where((e) => e.user?.id != user.id)
|
|
||||||
.where((e) => e.message.silent != true)
|
|
||||||
.where((e) => e.message.shadowed != true)
|
|
||||||
.listen((event) async {
|
|
||||||
final channel = client.channel(
|
|
||||||
event.channelType,
|
|
||||||
id: event.channelId,
|
|
||||||
);
|
|
||||||
|
|
||||||
client.showLocalNotification(
|
|
||||||
event.message,
|
|
||||||
ChannelModel(
|
|
||||||
id: channel.id,
|
|
||||||
createdAt: channel.createdAt,
|
|
||||||
extraData: channel.extraData,
|
|
||||||
type: channel.type,
|
|
||||||
memberCount: channel.memberCount,
|
|
||||||
frozen: channel.frozen,
|
|
||||||
cid: channel.cid,
|
|
||||||
deletedAt: channel.deletedAt,
|
|
||||||
config: channel.config,
|
|
||||||
createdBy: channel.createdBy,
|
|
||||||
updatedAt: channel.updatedAt,
|
|
||||||
lastMessageAt: channel.lastMessageAt,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
_disconnectTimer = Timer(client.backgroundKeepAlive, () {
|
|
||||||
client.disconnect();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
client.disconnect();
|
|
||||||
}
|
|
||||||
} else if (state == AppLifecycleState.resumed) {
|
|
||||||
_newMessageSubscription?.cancel();
|
|
||||||
if (_disconnectTimer?.isActive == true) {
|
|
||||||
_disconnectTimer.cancel();
|
|
||||||
} else {
|
|
||||||
if (client.wsConnectionStatus.value ==
|
|
||||||
ConnectionStatus.disconnected) {
|
|
||||||
NotificationService.handleIosMessageQueue(client);
|
|
||||||
client.connect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
WidgetsBinding.instance.removeObserver(this);
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:stream_chat/stream_chat.dart';
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
import 'package:stream_chat_flutter/src/channel_header.dart';
|
import 'package:stream_chat_flutter/src/channel_header.dart';
|
||||||
import 'package:stream_chat_flutter/src/channel_preview.dart';
|
import 'package:stream_chat_flutter/src/channel_preview.dart';
|
||||||
import 'package:stream_chat_flutter/src/message_input.dart';
|
import 'package:stream_chat_flutter/src/message_input.dart';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:stream_chat/stream_chat.dart';
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
|
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:lottie/lottie.dart';
|
import 'package:lottie/lottie.dart';
|
||||||
import 'package:stream_chat/stream_chat.dart';
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
import 'package:stream_chat_flutter/src/stream_channel.dart';
|
|
||||||
|
|
||||||
/// Widget to show the current list of typing users
|
/// Widget to show the current list of typing users
|
||||||
class TypingIndicator extends StatelessWidget {
|
class TypingIndicator extends StatelessWidget {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:stream_chat/stream_chat.dart';
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
|
|
||||||
import '../stream_chat_flutter.dart';
|
import '../stream_chat_flutter.dart';
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:jiffy/jiffy.dart';
|
import 'package:jiffy/jiffy.dart';
|
||||||
import 'package:stream_chat/stream_chat.dart';
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
||||||
import 'package:stream_chat_flutter/src/user_list_view.dart';
|
import 'package:stream_chat_flutter/src/user_list_view.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
import 'dart:convert';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:stream_chat/stream_chat.dart';
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
import 'package:stream_chat_flutter/src/lazy_load_scroll_view.dart';
|
|
||||||
import 'package:stream_chat_flutter/src/users_bloc.dart';
|
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
import 'user_item.dart';
|
import 'user_item.dart';
|
||||||
@@ -142,209 +138,168 @@ class _UserListViewState extends State<UserListView>
|
|||||||
with WidgetsBindingObserver {
|
with WidgetsBindingObserver {
|
||||||
bool get _isListView => widget.crossAxisCount == 1;
|
bool get _isListView => widget.crossAxisCount == 1;
|
||||||
|
|
||||||
@override
|
UserListController _userListController = UserListController();
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
final usersBloc = UsersBloc.of(context);
|
|
||||||
usersBloc.queryUsers(
|
|
||||||
filter: widget.filter,
|
|
||||||
sort: widget.sort,
|
|
||||||
pagination: widget.pagination,
|
|
||||||
options: widget.options,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final usersBloc = UsersBloc.of(context);
|
var child = UserListCore(
|
||||||
|
errorBuilder: (err) {
|
||||||
if (!widget.pullToRefresh) {
|
return _buildError(err);
|
||||||
return _buildListView(usersBloc);
|
},
|
||||||
}
|
emptyBuilder: (context) {
|
||||||
|
return _buildEmpty();
|
||||||
return RefreshIndicator(
|
},
|
||||||
onRefresh: () async {
|
loadingBuilder: (context) {
|
||||||
return usersBloc.queryUsers(
|
return LayoutBuilder(
|
||||||
filter: widget.filter,
|
builder: (context, viewportConstraints) {
|
||||||
sort: widget.sort,
|
return SingleChildScrollView(
|
||||||
options: widget.options,
|
physics: AlwaysScrollableScrollPhysics(),
|
||||||
pagination: widget.pagination,
|
child: ConstrainedBox(
|
||||||
|
constraints: BoxConstraints(
|
||||||
|
minHeight: viewportConstraints.maxHeight,
|
||||||
|
),
|
||||||
|
child: Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
child: _buildListView(usersBloc),
|
listBuilder: (context, list) {
|
||||||
|
return _buildListView(list);
|
||||||
|
},
|
||||||
|
pagination: widget.pagination,
|
||||||
|
options: widget.options,
|
||||||
|
sort: widget.sort,
|
||||||
|
filter: widget.filter,
|
||||||
|
groupAlphabetically: widget.groupAlphabetically,
|
||||||
|
userListController: _userListController,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (!widget.pullToRefresh) {
|
||||||
|
return child;
|
||||||
|
} else {
|
||||||
|
return RefreshIndicator(
|
||||||
|
onRefresh: () async {
|
||||||
|
_userListController.loadData();
|
||||||
|
},
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get isListAlreadySorted =>
|
bool get isListAlreadySorted =>
|
||||||
widget.sort?.any((e) => e.field == 'name' && e.direction == 1) ?? false;
|
widget.sort?.any((e) => e.field == 'name' && e.direction == 1) ?? false;
|
||||||
|
|
||||||
Stream<List<ListItem>> _buildUserStream(
|
Widget _buildError(Error error) {
|
||||||
UsersBlocState usersBlocState,
|
print((error).stackTrace);
|
||||||
) {
|
|
||||||
return usersBlocState.usersStream.map(
|
if (widget.errorBuilder != null) {
|
||||||
(users) {
|
return widget.errorBuilder(error);
|
||||||
if (widget.groupAlphabetically) {
|
}
|
||||||
var temp = users;
|
|
||||||
if (!isListAlreadySorted) {
|
var message = error.toString();
|
||||||
temp = users..sort((curr, next) => curr.name.compareTo(next.name));
|
if (error is DioError) {
|
||||||
}
|
final dioError = error as DioError;
|
||||||
final groupedUsers = <String, List<User>>{};
|
if (dioError.type == DioErrorType.RESPONSE) {
|
||||||
for (var e in temp) {
|
message = dioError.message;
|
||||||
final alphabet = e.name[0]?.toUpperCase();
|
} else {
|
||||||
groupedUsers[alphabet] = [...groupedUsers[alphabet] ?? [], e];
|
message = 'Check your connection and retry';
|
||||||
}
|
}
|
||||||
final items = <ListItem>[];
|
}
|
||||||
for (var key in groupedUsers.keys) {
|
return Center(
|
||||||
items.add(ListHeaderItem(key));
|
child: Column(
|
||||||
items.addAll(groupedUsers[key].map((e) => ListUserItem(e)));
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
}
|
children: <Widget>[
|
||||||
return items;
|
Text.rich(
|
||||||
}
|
TextSpan(
|
||||||
return users.map((e) => ListUserItem(e)).toList();
|
children: [
|
||||||
|
WidgetSpan(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
right: 2.0,
|
||||||
|
),
|
||||||
|
child: Icon(Icons.error_outline),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextSpan(text: 'Error loading channels'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
style: Theme.of(context).textTheme.headline6,
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
top: 16.0,
|
||||||
|
),
|
||||||
|
child: Text(message),
|
||||||
|
),
|
||||||
|
FlatButton(
|
||||||
|
onPressed: () {
|
||||||
|
_userListController.loadData();
|
||||||
|
},
|
||||||
|
child: Text('Retry'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildEmpty() {
|
||||||
|
if (widget.emptyBuilder != null) {
|
||||||
|
return widget.emptyBuilder(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
return LayoutBuilder(
|
||||||
|
builder: (context, viewportConstraints) {
|
||||||
|
return SingleChildScrollView(
|
||||||
|
physics: AlwaysScrollableScrollPhysics(),
|
||||||
|
child: ConstrainedBox(
|
||||||
|
constraints: BoxConstraints(
|
||||||
|
minHeight: viewportConstraints.maxHeight,
|
||||||
|
),
|
||||||
|
child: Center(
|
||||||
|
child: Text('There are no users currently'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
StreamBuilder<List<ListItem>> _buildListView(
|
Widget _buildListView(
|
||||||
UsersBlocState usersBlocState,
|
List<ListItem> items,
|
||||||
) {
|
) {
|
||||||
return StreamBuilder(
|
final child = _isListView
|
||||||
stream: _buildUserStream(usersBlocState),
|
? ListView.separated(
|
||||||
builder: (context, snapshot) {
|
physics: AlwaysScrollableScrollPhysics(),
|
||||||
if (snapshot.hasError) {
|
itemCount: items.isNotEmpty ? items.length + 1 : items.length,
|
||||||
if (snapshot.error is Error) {
|
separatorBuilder: (_, index) {
|
||||||
print((snapshot.error as Error).stackTrace);
|
if (widget.separatorBuilder != null) {
|
||||||
}
|
return widget.separatorBuilder(context, index);
|
||||||
|
}
|
||||||
if (widget.errorBuilder != null) {
|
return _separatorBuilder(context, index);
|
||||||
return widget.errorBuilder(snapshot.error);
|
},
|
||||||
}
|
itemBuilder: (context, index) {
|
||||||
|
return _listItemBuilder(context, index, items);
|
||||||
var message = snapshot.error.toString();
|
},
|
||||||
if (snapshot.error is DioError) {
|
)
|
||||||
final dioError = snapshot.error as DioError;
|
: GridView.builder(
|
||||||
if (dioError.type == DioErrorType.RESPONSE) {
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
message = dioError.message;
|
crossAxisCount: widget.crossAxisCount,
|
||||||
} else {
|
|
||||||
message = 'Check your connection and retry';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Center(
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: <Widget>[
|
|
||||||
Text.rich(
|
|
||||||
TextSpan(
|
|
||||||
children: [
|
|
||||||
WidgetSpan(
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
right: 2.0,
|
|
||||||
),
|
|
||||||
child: Icon(Icons.error_outline),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
TextSpan(text: 'Error loading channels'),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
style: Theme.of(context).textTheme.headline6,
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
top: 16.0,
|
|
||||||
),
|
|
||||||
child: Text(message),
|
|
||||||
),
|
|
||||||
FlatButton(
|
|
||||||
onPressed: () {
|
|
||||||
usersBlocState.queryUsers(
|
|
||||||
filter: widget.filter,
|
|
||||||
sort: widget.sort,
|
|
||||||
pagination: widget.pagination,
|
|
||||||
options: widget.options,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: Text('Retry'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
);
|
itemCount: items.isNotEmpty ? items.length + 1 : items.length,
|
||||||
}
|
physics: AlwaysScrollableScrollPhysics(),
|
||||||
|
itemBuilder: (context, index) {
|
||||||
if (!snapshot.hasData) {
|
return _gridItemBuilder(context, index, items);
|
||||||
return LayoutBuilder(
|
|
||||||
builder: (context, viewportConstraints) {
|
|
||||||
return SingleChildScrollView(
|
|
||||||
physics: AlwaysScrollableScrollPhysics(),
|
|
||||||
child: ConstrainedBox(
|
|
||||||
constraints: BoxConstraints(
|
|
||||||
minHeight: viewportConstraints.maxHeight,
|
|
||||||
),
|
|
||||||
child: Center(
|
|
||||||
child: CircularProgressIndicator(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
final items = snapshot.data;
|
return LazyLoadScrollView(
|
||||||
|
onEndOfPage: () async {
|
||||||
if (items.isEmpty && widget.emptyBuilder != null) {
|
return _userListController.paginateData();
|
||||||
return widget.emptyBuilder(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (items.isEmpty && widget.emptyBuilder == null) {
|
|
||||||
return LayoutBuilder(
|
|
||||||
builder: (context, viewportConstraints) {
|
|
||||||
return SingleChildScrollView(
|
|
||||||
physics: AlwaysScrollableScrollPhysics(),
|
|
||||||
child: ConstrainedBox(
|
|
||||||
constraints: BoxConstraints(
|
|
||||||
minHeight: viewportConstraints.maxHeight,
|
|
||||||
),
|
|
||||||
child: Center(
|
|
||||||
child: Text('There are no users currently'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final child = _isListView
|
|
||||||
? ListView.separated(
|
|
||||||
physics: AlwaysScrollableScrollPhysics(),
|
|
||||||
itemCount: items.isNotEmpty ? items.length + 1 : items.length,
|
|
||||||
separatorBuilder: (_, index) {
|
|
||||||
if (widget.separatorBuilder != null) {
|
|
||||||
return widget.separatorBuilder(context, index);
|
|
||||||
}
|
|
||||||
return _separatorBuilder(context, index);
|
|
||||||
},
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
return _listItemBuilder(context, index, items);
|
|
||||||
},
|
|
||||||
)
|
|
||||||
: GridView.builder(
|
|
||||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
|
||||||
crossAxisCount: widget.crossAxisCount,
|
|
||||||
),
|
|
||||||
itemCount: items.isNotEmpty ? items.length + 1 : items.length,
|
|
||||||
physics: AlwaysScrollableScrollPhysics(),
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
return _gridItemBuilder(context, index, items);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
return LazyLoadScrollView(
|
|
||||||
onEndOfPage: () async {
|
|
||||||
return _listenUserPagination(usersBlocState);
|
|
||||||
},
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
|
child: child,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -483,72 +438,4 @@ class _UserListViewState extends State<UserListView>
|
|||||||
color: StreamChatTheme.of(context).colorTheme.greyWhisper,
|
color: StreamChatTheme.of(context).colorTheme.greyWhisper,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _listenUserPagination(UsersBlocState usersProvider) {
|
|
||||||
usersProvider.queryUsers(
|
|
||||||
filter: widget.filter,
|
|
||||||
sort: widget.sort,
|
|
||||||
pagination: widget.pagination.copyWith(
|
|
||||||
offset: usersProvider.users?.length ?? 0,
|
|
||||||
),
|
|
||||||
options: widget.options,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void didUpdateWidget(UserListView oldWidget) {
|
|
||||||
super.didUpdateWidget(oldWidget);
|
|
||||||
if (widget.filter?.toString() != oldWidget.filter?.toString() ||
|
|
||||||
jsonEncode(widget.sort) != jsonEncode(oldWidget.sort) ||
|
|
||||||
widget.pagination?.toJson()?.toString() !=
|
|
||||||
oldWidget.pagination?.toJson()?.toString() ||
|
|
||||||
widget.options?.toString() != oldWidget.options?.toString()) {
|
|
||||||
final usersBloc = UsersBloc.of(context);
|
|
||||||
usersBloc.queryUsers(
|
|
||||||
filter: widget.filter,
|
|
||||||
sort: widget.sort,
|
|
||||||
pagination: widget.pagination,
|
|
||||||
options: widget.options,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class ListItem {
|
|
||||||
String get key {
|
|
||||||
if (this is ListHeaderItem) {
|
|
||||||
final header = (this as ListHeaderItem).heading;
|
|
||||||
return 'HEADER-$header';
|
|
||||||
}
|
|
||||||
if (this is ListUserItem) {
|
|
||||||
final user = (this as ListUserItem).user;
|
|
||||||
return 'USER-${user.id}';
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget when({
|
|
||||||
@required Widget Function(String heading) headerItem,
|
|
||||||
@required Widget Function(User user) userItem,
|
|
||||||
}) {
|
|
||||||
if (this is ListHeaderItem) {
|
|
||||||
return headerItem((this as ListHeaderItem).heading);
|
|
||||||
}
|
|
||||||
if (this is ListUserItem) {
|
|
||||||
return userItem((this as ListUserItem).user);
|
|
||||||
}
|
|
||||||
return SizedBox();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ListHeaderItem extends ListItem {
|
|
||||||
final String heading;
|
|
||||||
|
|
||||||
ListHeaderItem(this.heading);
|
|
||||||
}
|
|
||||||
|
|
||||||
class ListUserItem extends ListItem {
|
|
||||||
final User user;
|
|
||||||
|
|
||||||
ListUserItem(this.user);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:stream_chat/stream_chat.dart';
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
import 'package:stream_chat_flutter/src/user_avatar.dart';
|
import 'package:stream_chat_flutter/src/user_avatar.dart';
|
||||||
|
|
||||||
class UserReactionDisplay extends StatelessWidget {
|
class UserReactionDisplay extends StatelessWidget {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:stream_chat/stream_chat.dart';
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
import '../stream_chat_flutter.dart';
|
import '../stream_chat_flutter.dart';
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
export 'package:stream_chat/stream_chat.dart';
|
|
||||||
|
|
||||||
export 'src/back_button.dart';
|
export 'src/back_button.dart';
|
||||||
export 'src/channel_header.dart';
|
export 'src/channel_header.dart';
|
||||||
export 'src/channel_image.dart';
|
export 'src/channel_image.dart';
|
||||||
@@ -7,7 +5,6 @@ export 'src/channel_list_header.dart';
|
|||||||
export 'src/channel_list_view.dart';
|
export 'src/channel_list_view.dart';
|
||||||
export 'src/channel_name.dart';
|
export 'src/channel_name.dart';
|
||||||
export 'src/channel_preview.dart';
|
export 'src/channel_preview.dart';
|
||||||
export 'src/channels_bloc.dart';
|
|
||||||
export 'src/date_divider.dart';
|
export 'src/date_divider.dart';
|
||||||
export 'src/deleted_message.dart';
|
export 'src/deleted_message.dart';
|
||||||
export 'src/file_attachment.dart';
|
export 'src/file_attachment.dart';
|
||||||
@@ -22,8 +19,6 @@ export 'src/message_text.dart';
|
|||||||
export 'src/message_widget.dart';
|
export 'src/message_widget.dart';
|
||||||
export 'src/reaction_picker.dart';
|
export 'src/reaction_picker.dart';
|
||||||
export 'src/sending_indicator.dart';
|
export 'src/sending_indicator.dart';
|
||||||
export 'src/stream_channel.dart';
|
|
||||||
export 'src/stream_chat.dart';
|
|
||||||
export 'src/stream_chat_theme.dart';
|
export 'src/stream_chat_theme.dart';
|
||||||
export 'src/stream_neumorphic_button.dart';
|
export 'src/stream_neumorphic_button.dart';
|
||||||
export 'src/stream_svg_icon.dart';
|
export 'src/stream_svg_icon.dart';
|
||||||
@@ -35,11 +30,8 @@ export 'src/user_item.dart';
|
|||||||
export 'src/user_item.dart';
|
export 'src/user_item.dart';
|
||||||
export 'src/user_list_view.dart';
|
export 'src/user_list_view.dart';
|
||||||
export 'src/user_list_view.dart';
|
export 'src/user_list_view.dart';
|
||||||
export 'src/users_bloc.dart';
|
|
||||||
export 'src/users_bloc.dart';
|
|
||||||
export 'src/utils.dart';
|
export 'src/utils.dart';
|
||||||
export 'src/video_attachment.dart';
|
export 'src/video_attachment.dart';
|
||||||
export 'src/message_search_bloc.dart';
|
|
||||||
export 'src/message_search_item.dart';
|
export 'src/message_search_item.dart';
|
||||||
export 'src/message_search_list_view.dart';
|
export 'src/message_search_list_view.dart';
|
||||||
export 'src/unread_indicator.dart';
|
export 'src/unread_indicator.dart';
|
||||||
@@ -47,3 +39,5 @@ export 'src/option_list_tile.dart';
|
|||||||
export 'src/channel_file_display_screen.dart';
|
export 'src/channel_file_display_screen.dart';
|
||||||
export 'src/channel_media_display_screen.dart';
|
export 'src/channel_media_display_screen.dart';
|
||||||
export 'src/info_tile.dart';
|
export 'src/info_tile.dart';
|
||||||
|
export 'src/stream_chat.dart';
|
||||||
|
export 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
name: stream_chat_flutter
|
name: stream_chat_flutter
|
||||||
homepage: https://github.com/GetStream/stream-chat-flutter
|
homepage: https://github.com/GetStream/stream-chat-flutter
|
||||||
description: Stream Chat official Flutter SDK. Build your own chat experience using Dart and Flutter.
|
description: Stream Chat official Flutter SDK. Build your own chat experience using Dart and Flutter.
|
||||||
version: 0.2.13+1
|
version: 1.0.0-rc
|
||||||
repository: https://github.com/GetStream/stream-chat-flutter
|
repository: https://github.com/GetStream/stream-chat-flutter
|
||||||
issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues
|
issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues
|
||||||
|
|
||||||
@@ -11,6 +11,9 @@ environment:
|
|||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
# TODO: Change over to main dependency when deployed
|
||||||
|
stream_chat_flutter_core:
|
||||||
|
path: ../stream_chat_flutter_core
|
||||||
flutter_app_badger: ^1.1.2
|
flutter_app_badger: ^1.1.2
|
||||||
photo_view: ^0.10.3
|
photo_view: ^0.10.3
|
||||||
rxdart: ^0.24.1
|
rxdart: ^0.24.1
|
||||||
@@ -29,7 +32,6 @@ dependencies:
|
|||||||
image_picker: ^0.6.7+17
|
image_picker: ^0.6.7+17
|
||||||
flutter_keyboard_visibility: ^4.0.2
|
flutter_keyboard_visibility: ^4.0.2
|
||||||
mime: ^0.9.7
|
mime: ^0.9.7
|
||||||
stream_chat: ^0.2.24+1
|
|
||||||
video_compress: ^2.1.1
|
video_compress: ^2.1.1
|
||||||
visibility_detector: ^0.1.5
|
visibility_detector: ^0.1.5
|
||||||
http_parser: ^3.1.4
|
http_parser: ^3.1.4
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:mockito/mockito.dart';
|
import 'package:mockito/mockito.dart';
|
||||||
import 'package:stream_chat/stream_chat.dart';
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
|
|
||||||
class MockClient extends Mock implements Client {}
|
class MockClient extends Mock implements Client {}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
# Miscellaneous
|
||||||
|
*.class
|
||||||
|
*.log
|
||||||
|
*.pyc
|
||||||
|
*.swp
|
||||||
|
.DS_Store
|
||||||
|
.atom/
|
||||||
|
.buildlog/
|
||||||
|
.history
|
||||||
|
.svn/
|
||||||
|
|
||||||
|
# IntelliJ related
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# The .vscode folder contains launch configuration and tasks you configure in
|
||||||
|
# VS Code which you may wish to be included in version control, so this line
|
||||||
|
# is commented out by default.
|
||||||
|
#.vscode/
|
||||||
|
|
||||||
|
# Flutter/Dart/Pub related
|
||||||
|
**/doc/api/
|
||||||
|
.dart_tool/
|
||||||
|
.flutter-plugins
|
||||||
|
.flutter-plugins-dependencies
|
||||||
|
.packages
|
||||||
|
.pub-cache/
|
||||||
|
.pub/
|
||||||
|
build/
|
||||||
|
|
||||||
|
# Android related
|
||||||
|
**/android/**/gradle-wrapper.jar
|
||||||
|
**/android/.gradle
|
||||||
|
**/android/captures/
|
||||||
|
**/android/gradlew
|
||||||
|
**/android/gradlew.bat
|
||||||
|
**/android/local.properties
|
||||||
|
**/android/**/GeneratedPluginRegistrant.java
|
||||||
|
|
||||||
|
# iOS/XCode related
|
||||||
|
**/ios/**/*.mode1v3
|
||||||
|
**/ios/**/*.mode2v3
|
||||||
|
**/ios/**/*.moved-aside
|
||||||
|
**/ios/**/*.pbxuser
|
||||||
|
**/ios/**/*.perspectivev3
|
||||||
|
**/ios/**/*sync/
|
||||||
|
**/ios/**/.sconsign.dblite
|
||||||
|
**/ios/**/.tags*
|
||||||
|
**/ios/**/.vagrant/
|
||||||
|
**/ios/**/DerivedData/
|
||||||
|
**/ios/**/Icon?
|
||||||
|
**/ios/**/Pods/
|
||||||
|
**/ios/**/.symlinks/
|
||||||
|
**/ios/**/profile
|
||||||
|
**/ios/**/xcuserdata
|
||||||
|
**/ios/.generated/
|
||||||
|
**/ios/Flutter/App.framework
|
||||||
|
**/ios/Flutter/Flutter.framework
|
||||||
|
**/ios/Flutter/Flutter.podspec
|
||||||
|
**/ios/Flutter/Generated.xcconfig
|
||||||
|
**/ios/Flutter/app.flx
|
||||||
|
**/ios/Flutter/app.zip
|
||||||
|
**/ios/Flutter/flutter_assets/
|
||||||
|
**/ios/Flutter/flutter_export_environment.sh
|
||||||
|
**/ios/ServiceDefinitions.json
|
||||||
|
**/ios/Runner/GeneratedPluginRegistrant.*
|
||||||
|
|
||||||
|
# Exceptions to above rules.
|
||||||
|
!**/ios/**/default.mode1v3
|
||||||
|
!**/ios/**/default.mode2v3
|
||||||
|
!**/ios/**/default.pbxuser
|
||||||
|
!**/ios/**/default.perspectivev3
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
# This file tracks properties of this Flutter project.
|
||||||
|
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||||
|
#
|
||||||
|
# This file should be version controlled and should not be manually edited.
|
||||||
|
|
||||||
|
version:
|
||||||
|
revision: 78910062997c3a836feee883712c241a5fd22983
|
||||||
|
channel: stable
|
||||||
|
|
||||||
|
project_type: package
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
## 1.0.0-rc
|
||||||
|
|
||||||
|
* First release
|
||||||
@@ -0,0 +1,219 @@
|
|||||||
|
SOURCE CODE LICENSE AGREEMENT
|
||||||
|
|
||||||
|
IMPORTANT - READ THIS CAREFULLY BEFORE DOWNLOADING, INSTALLING, USING OR
|
||||||
|
ELECTRONICALLY ACCESSING THIS PROPRIETARY PRODUCT.
|
||||||
|
|
||||||
|
THIS IS A LEGAL AGREEMENT BETWEEN STREAM.IO, INC. (“STREAM.IO”) AND THE
|
||||||
|
BUSINESS ENTITY OR PERSON FOR WHOM YOU (“YOU”) ARE ACTING (“CUSTOMER”) AS THE
|
||||||
|
LICENSEE OF THE PROPRIETARY SOFTWARE INTO WHICH THIS AGREEMENT HAS BEEN
|
||||||
|
INCLUDED (THE “AGREEMENT”). YOU AGREE THAT YOU ARE THE CUSTOMER, OR YOU ARE AN
|
||||||
|
EMPLOYEE OR AGENT OF CUSTOMER AND ARE ENTERING INTO THIS AGREEMENT FOR LICENSE
|
||||||
|
OF THE SOFTWARE BY CUSTOMER FOR CUSTOMER’S BUSINESS PURPOSES AS DESCRIBED IN
|
||||||
|
AND IN ACCORDANCE WITH THIS AGREEMENT. YOU HEREBY AGREE THAT YOU ENTER INTO
|
||||||
|
THIS AGREEMENT ON BEHALF OF CUSTOMER AND THAT YOU HAVE THE AUTHORITY TO BIND
|
||||||
|
CUSTOMER TO THIS AGREEMENT.
|
||||||
|
|
||||||
|
STREAM.IO IS WILLING TO LICENSE THE SOFTWARE TO CUSTOMER ONLY ON THE FOLLOWING
|
||||||
|
CONDITIONS: (1) YOU ARE A CURRENT CUSTOMER OF STREAM.IO; (2) YOU ARE NOT A
|
||||||
|
COMPETITOR OF STREAM.IO; AND (3) THAT YOU ACCEPT ALL THE TERMS IN THIS
|
||||||
|
AGREEMENT. BY DOWNLOADING, INSTALLING, CONFIGURING, ACCESSING OR OTHERWISE
|
||||||
|
USING THE SOFTWARE, INCLUDING ANY UPDATES, UPGRADES, OR NEWER VERSIONS, YOU
|
||||||
|
REPRESENT, WARRANT AND ACKNOWLEDGE THAT (A) CUSTOMER IS A CURRENT CUSTOMER OF
|
||||||
|
STREAM.IO; (B) CUSTOMER IS NOT A COMPETITOR OF STREAM.IO; AND THAT (C) YOU HAVE
|
||||||
|
READ THIS AGREEMENT, UNDERSTAND THIS AGREEMENT, AND THAT CUSTOMER AGREES TO BE
|
||||||
|
BOUND BY ALL THE TERMS OF THIS AGREEMENT.
|
||||||
|
|
||||||
|
IF YOU DO NOT AGREE TO ALL THE TERMS AND CONDITIONS OF THIS AGREEMENT,
|
||||||
|
STREAM.IO IS UNWILLING TO LICENSE THE SOFTWARE TO CUSTOMER, AND THEREFORE, DO
|
||||||
|
NOT COMPLETE THE DOWNLOAD PROCESS, ACCESS OR OTHERWISE USE THE SOFTWARE, AND
|
||||||
|
CUSTOMER SHOULD IMMEDIATELY RETURN THE SOFTWARE AND CEASE ANY USE OF THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
1. SOFTWARE. The Stream.io software accompanying this Agreement, may include
|
||||||
|
Source Code, Executable Object Code, associated media, printed materials and
|
||||||
|
documentation (collectively, the “Software”). The Software also includes any
|
||||||
|
updates or upgrades to or new versions of the original Software, if and when
|
||||||
|
made available to you by Stream.io. “Source Code” means computer programming
|
||||||
|
code in human readable form that is not suitable for machine execution without
|
||||||
|
the intervening steps of interpretation or compilation. “Executable Object
|
||||||
|
Code" means the computer programming code in any other form than Source Code
|
||||||
|
that is not readily perceivable by humans and suitable for machine execution
|
||||||
|
without the intervening steps of interpretation or compilation. “Site” means a
|
||||||
|
Customer location controlled by Customer. “Authorized User” means any employee
|
||||||
|
or contractor of Customer working at the Site, who has signed a written
|
||||||
|
confidentiality agreement with Customer or is otherwise bound in writing by
|
||||||
|
confidentiality and use obligations at least as restrictive as those imposed
|
||||||
|
under this Agreement.
|
||||||
|
|
||||||
|
2. LICENSE GRANT. Subject to the terms and conditions of this Agreement, in
|
||||||
|
consideration for the representations, warranties, and covenants made by
|
||||||
|
Customer in this Agreement, Stream.io grants to Customer, during the term of
|
||||||
|
this Agreement, a personal, non-exclusive, non-transferable, non-sublicensable
|
||||||
|
license to:
|
||||||
|
|
||||||
|
a. install and use Software Source Code on password protected computers at a Site,
|
||||||
|
restricted to Authorized Users;
|
||||||
|
|
||||||
|
b. create derivative works, improvements (whether or not patentable), extensions
|
||||||
|
and other modifications to the Software Source Code (“Modifications”) to build
|
||||||
|
unique scalable newsfeeds, activity streams, and in-app messaging via Stream’s
|
||||||
|
application program interface (“API”);
|
||||||
|
|
||||||
|
c. compile the Software Source Code to create Executable Object Code versions of
|
||||||
|
the Software Source Code and Modifications to build such newsfeeds, activity
|
||||||
|
streams, and in-app messaging via the API;
|
||||||
|
|
||||||
|
d. install, execute and use such Executable Object Code versions solely for
|
||||||
|
Customer’s internal business use (including development of websites through
|
||||||
|
which data generated by Stream services will be streamed (“Apps”));
|
||||||
|
|
||||||
|
e. use and distribute such Executable Object Code as part of Customer’s Apps; and
|
||||||
|
|
||||||
|
f. make electronic copies of the Software and Modifications as required for backup
|
||||||
|
or archival purposes.
|
||||||
|
|
||||||
|
3. RESTRICTIONS. Customer is responsible for all activities that occur in
|
||||||
|
connection with the Software. Customer will not, and will not attempt to: (a)
|
||||||
|
sublicense or transfer the Software or any Source Code related to the Software
|
||||||
|
or any of Customer’s rights under this Agreement, except as otherwise provided
|
||||||
|
in this Agreement, (b) use the Software Source Code for the benefit of a third
|
||||||
|
party or to operate a service; (c) allow any third party to access or use the
|
||||||
|
Software Source Code; (d) sublicense or distribute the Software Source Code or
|
||||||
|
any Modifications in Source Code or other derivative works based on any part of
|
||||||
|
the Software Source Code; (e) use the Software in any manner that competes with
|
||||||
|
Stream.io or its business; or (e) otherwise use the Software in any manner that
|
||||||
|
exceeds the scope of use permitted in this Agreement. Customer shall use the
|
||||||
|
Software in compliance with any accompanying documentation any laws applicable
|
||||||
|
to Customer.
|
||||||
|
|
||||||
|
4. OPEN SOURCE. Customer and its Authorized Users shall not use any software or
|
||||||
|
software components that are open source in conjunction with the Software
|
||||||
|
Source Code or any Modifications in Source Code or in any way that could
|
||||||
|
subject the Software to any open source licenses.
|
||||||
|
|
||||||
|
5. CONTRACTORS. Under the rights granted to Customer under this Agreement,
|
||||||
|
Customer may permit its employees, contractors, and agencies of Customer to
|
||||||
|
become Authorized Users to exercise the rights to the Software granted to
|
||||||
|
Customer in accordance with this Agreement solely on behalf of Customer to
|
||||||
|
provide services to Customer; provided that Customer shall be liable for the
|
||||||
|
acts and omissions of all Authorized Users to the extent any of such acts or
|
||||||
|
omissions, if performed by Customer, would constitute a breach of, or otherwise
|
||||||
|
give rise to liability to Customer under, this Agreement. Customer shall not
|
||||||
|
and shall not permit any Authorized User to use the Software except as
|
||||||
|
expressly permitted in this Agreement.
|
||||||
|
|
||||||
|
6. COMPETITIVE PRODUCT DEVELOPMENT. Customer shall not use the Software in any way
|
||||||
|
to engage in the development of products or services which could be reasonably
|
||||||
|
construed to provide a complete or partial functional or commercial alternative
|
||||||
|
to Stream.io’s products or services (a “Competitive Product”). Customer shall
|
||||||
|
ensure that there is no direct or indirect use of, or sharing of, Software
|
||||||
|
source code, or other information based upon or derived from the Software to
|
||||||
|
develop such products or services. Without derogating from the generality of
|
||||||
|
the foregoing, development of Competitive Products shall include having direct
|
||||||
|
or indirect access to, supervising, consulting or assisting in the development
|
||||||
|
of, or producing any specifications, documentation, object code or source code
|
||||||
|
for, all or part of a Competitive Product.
|
||||||
|
|
||||||
|
7. LIMITATION ON MODIFICATIONS. Notwithstanding any provision in this Agreement,
|
||||||
|
Modifications may only be created and used by Customer as permitted by this
|
||||||
|
Agreement and Modification Source Code may not be distributed to third parties.
|
||||||
|
Customer will not assert against Stream.io, its affiliates, or their customers,
|
||||||
|
direct or indirect, agents and contractors, in any way, any patent rights that
|
||||||
|
Customer may obtain relating to any Modifications for Stream.io, its
|
||||||
|
affiliates’, or their customers’, direct or indirect, agents’ and contractors’
|
||||||
|
manufacture, use, import, offer for sale or sale of any Stream.io products or
|
||||||
|
services.
|
||||||
|
|
||||||
|
8. DELIVERY AND ACCEPTANCE. The Software will be delivered electronically pursuant
|
||||||
|
to Stream.io standard download procedures. The Software is deemed accepted upon
|
||||||
|
delivery.
|
||||||
|
|
||||||
|
9. IMPLEMENTATION AND SUPPORT. Stream.io has no obligation under this Agreement to
|
||||||
|
provide any support or consultation concerning the Software.
|
||||||
|
|
||||||
|
10. TERM AND TERMINATION. The term of this Agreement begins when the Software is
|
||||||
|
downloaded or accessed and shall continue until terminated. Either party may
|
||||||
|
terminate this Agreement upon written notice. This Agreement shall
|
||||||
|
automatically terminate if Customer is or becomes a competitor of Stream.io or
|
||||||
|
makes or sells any Competitive Products. Upon termination of this Agreement for
|
||||||
|
any reason, (a) all rights granted to Customer in this Agreement immediately
|
||||||
|
cease to exist, (b) Customer must promptly discontinue all use of the Software
|
||||||
|
and return to Stream.io or destroy all copies of the Software in Customer’s
|
||||||
|
possession or control. Any continued use of the Software by Customer or attempt
|
||||||
|
by Customer to exercise any rights under this Agreement after this Agreement
|
||||||
|
has terminated shall be considered copyright infringement and subject Customer
|
||||||
|
to applicable remedies for copyright infringement. Sections 2, 5, 6, 8 and 9
|
||||||
|
shall survive expiration or termination of this Agreement for any reason.
|
||||||
|
|
||||||
|
11. OWNERSHIP. As between the parties, the Software and all worldwide intellectual
|
||||||
|
property rights and proprietary rights relating thereto or embodied therein,
|
||||||
|
are the exclusive property of Stream.io and its suppliers. Stream.io and its
|
||||||
|
suppliers reserve all rights in and to the Software not expressly granted to
|
||||||
|
Customer in this Agreement, and no other licenses or rights are granted by
|
||||||
|
implication, estoppel or otherwise.
|
||||||
|
|
||||||
|
12. WARRANTY DISCLAIMER. USE OF THIS SOFTWARE IS ENTIRELY AT YOURS AND CUSTOMER’S
|
||||||
|
OWN RISK. THE SOFTWARE IS PROVIDED “AS IS” WITHOUT ANY WARRANTY OF ANY KIND
|
||||||
|
WHATSOEVER. STREAM.IO DOES NOT MAKE, AND HEREBY DISCLAIMS, ANY WARRANTY OF ANY
|
||||||
|
KIND, WHETHER EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING WITHOUT
|
||||||
|
LIMITATION, THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE, TITLE, NON-INFRINGEMENT OF THIRD-PARTY RIGHTS, RESULTS, EFFORTS,
|
||||||
|
QUALITY OR QUIET ENJOYMENT. STREAM.IO DOES NOT WARRANT THAT THE SOFTWARE IS
|
||||||
|
ERROR-FREE, WILL FUNCTION WITHOUT INTERRUPTION, WILL MEET ANY SPECIFIC NEED
|
||||||
|
THAT CUSTOMER HAS, THAT ALL DEFECTS WILL BE CORRECTED OR THAT IT IS
|
||||||
|
SUFFICIENTLY DOCUMENTED TO BE USABLE BY CUSTOMER. TO THE EXTENT THAT STREAM.IO
|
||||||
|
MAY NOT DISCLAIM ANY WARRANTY AS A MATTER OF APPLICABLE LAW, THE SCOPE AND
|
||||||
|
DURATION OF SUCH WARRANTY WILL BE THE MINIMUM PERMITTED UNDER SUCH LAW.
|
||||||
|
CUSTOMER ACKNOWLEDGES THAT IT HAS RELIED ON NO WARRANTIES OTHER THAN THE
|
||||||
|
EXPRESS WARRANTIES IN THIS AGREEMENT.
|
||||||
|
|
||||||
|
13. LIMITATION OF LIABILITY. TO THE FULLEST EXTENT PERMISSIBLE BY LAW, STREAM.IO’S
|
||||||
|
TOTAL LIABILITY FOR ALL DAMAGES ARISING OUT OF OR RELATED TO THE SOFTWARE OR
|
||||||
|
THIS AGREEMENT, WHETHER IN CONTRACT, TORT (INCLUDING NEGLIGENCE) OR OTHERWISE,
|
||||||
|
SHALL NOT EXCEED $100. IN NO EVENT WILL STREAM.IO BE LIABLE FOR ANY INDIRECT,
|
||||||
|
CONSEQUENTIAL, EXEMPLARY, PUNITIVE, SPECIAL OR INCIDENTAL DAMAGES OF ANY KIND
|
||||||
|
WHATSOEVER, INCLUDING ANY LOST DATA AND LOST PROFITS, ARISING FROM OR RELATING
|
||||||
|
TO THE SOFTWARE EVEN IF STREAM.IO HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||||
|
DAMAGES. CUSTOMER ACKNOWLEDGES THAT THIS PROVISION REFLECTS THE AGREED UPON
|
||||||
|
ALLOCATION OF RISK FOR THIS AGREEMENT AND THAT STREAM.IO WOULD NOT ENTER INTO
|
||||||
|
THIS AGREEMENT WITHOUT THESE LIMITATIONS ON ITS LIABILITY.
|
||||||
|
|
||||||
|
14. General. Customer may not assign or transfer this Agreement, by operation of
|
||||||
|
law or otherwise, or any of its rights under this Agreement (including the
|
||||||
|
license rights granted to Customer) to any third party without Stream.io’s
|
||||||
|
prior written consent, which consent will not be unreasonably withheld or
|
||||||
|
delayed. Stream.io may assign this Agreement, without consent, including, but
|
||||||
|
limited to, affiliate or any successor to all or substantially all its business
|
||||||
|
or assets to which this Agreement relates, whether by merger, sale of assets,
|
||||||
|
sale of stock, reorganization or otherwise. Any attempted assignment or
|
||||||
|
transfer in violation of the foregoing will be null and void. Stream.io shall
|
||||||
|
not be liable hereunder by reason of any failure or delay in the performance of
|
||||||
|
its obligations hereunder for any cause which is beyond the reasonable control.
|
||||||
|
All notices, consents, and approvals under this Agreement must be delivered in
|
||||||
|
writing by courier, by electronic mail, or by certified or registered mail,
|
||||||
|
(postage prepaid and return receipt requested) to the other party at the
|
||||||
|
address set forth in the customer agreement between Stream.io and Customer and
|
||||||
|
will be effective upon receipt or when delivery is refused. This Agreement will
|
||||||
|
be governed by and interpreted in accordance with the laws of the State of
|
||||||
|
Colorado, without reference to its choice of laws rules. The United Nations
|
||||||
|
Convention on Contracts for the International Sale of Goods does not apply to
|
||||||
|
this Agreement. Any action or proceeding arising from or relating to this
|
||||||
|
Agreement shall be brought in a federal or state court in Denver, Colorado, and
|
||||||
|
each party irrevocably submits to the jurisdiction and venue of any such court
|
||||||
|
in any such action or proceeding. All waivers must be in writing. Any waiver or
|
||||||
|
failure to enforce any provision of this Agreement on one occasion will not be
|
||||||
|
deemed a waiver of any other provision or of such provision on any other
|
||||||
|
occasion. If any provision of this Agreement is unenforceable, such provision
|
||||||
|
will be changed and interpreted to accomplish the objectives of such provision
|
||||||
|
to the greatest extent possible under applicable law and the remaining
|
||||||
|
provisions will continue in full force and effect. Customer shall not violate
|
||||||
|
any applicable law, rule or regulation, including those regarding the export of
|
||||||
|
technical data. The headings of Sections of this Agreement are for convenience
|
||||||
|
and are not to be used in interpreting this Agreement. As used in this
|
||||||
|
Agreement, the word “including” means “including but not limited to.” This
|
||||||
|
Agreement (including all exhibits and attachments) constitutes the entire
|
||||||
|
agreement between the parties regarding the subject hereof and supersedes all
|
||||||
|
prior or contemporaneous agreements, understandings and communication, whether
|
||||||
|
written or oral. This Agreement may be amended only by a written document
|
||||||
|
signed by both parties. The terms of any purchase order or similar document
|
||||||
|
submitted by Customer to Stream.io will have no effect.
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
# Official Flutter SDK Core for [Stream Chat](https://getstream.io/chat/)
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://getstream.io/tutorials/ios-chat/"><img src="https://i.imgur.com/L4Mj8S2.png" alt="Flutter Chat" width="60%" /></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
> The official Flutter core components for Stream Chat, a service for
|
||||||
|
> building chat applications.
|
||||||
|
|
||||||
|
[](https://pub.dartlang.org/packages/stream_chat_flutter)
|
||||||
|

|
||||||
|
[](https://gitter.im/GetStream/stream-chat-flutter?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||||
|

|
||||||
|
<img align="right" src="https://getstream.imgix.net/images/ios-chat-tutorial/iphone_chat_art@1x.png?auto=format,enhance" width="50%" />
|
||||||
|
|
||||||
|
**Quick Links**
|
||||||
|
|
||||||
|
- [Register](https://getstream.io/chat/trial/) to get an API key for Stream Chat
|
||||||
|
- [Flutter Chat Tutorial](https://getstream.io/chat/flutter/tutorial/)
|
||||||
|
- [Chat UI Kit](https://getstream.io/chat/ui-kit/)
|
||||||
|
|
||||||
|
## Flutter Chat Tutorial
|
||||||
|
|
||||||
|
The best place to start is the [Flutter Chat Tutorial](https://getstream.io/chat/flutter/tutorial/).
|
||||||
|
It teaches you how to use this SDK and also shows how to make frequently required changes.
|
||||||
|
|
||||||
|
## Example App
|
||||||
|
|
||||||
|
This repo includes a fully functional example app with setup instructions.
|
||||||
|
The example is available under the [example](https://github.com/GetStream/stream-chat-flutter-core/tree/master/example) folder.
|
||||||
|
|
||||||
|
## Add dependency
|
||||||
|
Add this to your package's pubspec.yaml file, use the latest version [](https://pub.dartlang.org/packages/stream_chat_flutter)
|
||||||
|
```yaml
|
||||||
|
dependencies:
|
||||||
|
stream_chat_flutter_core: ^latest_version
|
||||||
|
```
|
||||||
|
|
||||||
|
You should then run `flutter packages get`
|
||||||
|
|
||||||
|
This package requires no custom setup on any platform since it does not depend on any platform-specific dependency
|
||||||
|
|
||||||
|
## Docs
|
||||||
|
|
||||||
|
This package provides business logic to fetch common things required for integrating Stream Chat into your application.
|
||||||
|
The core package allows more customisation and hence provides business logic but no UI components.
|
||||||
|
Please use the stream_chat_flutter package for the full fledged suite of UI components or stream_chat for the low-level client.
|
||||||
|
|
||||||
|
The package primarily contains three types of classes:
|
||||||
|
|
||||||
|
1) Business Logic Components
|
||||||
|
2) Core Components
|
||||||
|
3) Core Controllers
|
||||||
|
|
||||||
|
### Business Logic Components
|
||||||
|
|
||||||
|
These components allow you to have the maximum and lower-level control of the queries being executed.
|
||||||
|
The BLoCs we provide are:
|
||||||
|
|
||||||
|
1) ChannelsBloc
|
||||||
|
2) MessageSearchBloc
|
||||||
|
3) UsersBloc
|
||||||
|
|
||||||
|
### Core Components
|
||||||
|
|
||||||
|
Core components usually are an easy way to fetch data associated with Stream Chat which are decoupled from UI and often expose UI builders.
|
||||||
|
Data fetching can be controlled with the controllers of the respective core components.
|
||||||
|
|
||||||
|
1) ChannelListCore (Fetch a list of channels)
|
||||||
|
2) MessageListCore (Fetch a list of messages from a channel)
|
||||||
|
3) MessageSearchListCore (Fetch a list of search messages)
|
||||||
|
4) UserListCore (Fetch a list of users)
|
||||||
|
5) StreamChatCore (This is different from the other core components - it is a version of StreamChat decoupled from theme and initialisations.)
|
||||||
|
|
||||||
|
### Core Controllers
|
||||||
|
|
||||||
|
Core Controllers are supplied to respective CoreList widgets which allows reloading and pagination of data whenever needed.
|
||||||
|
|
||||||
|
1) ChannelListController
|
||||||
|
2) MessageListController
|
||||||
|
3) MessageSearchListController
|
||||||
|
4) ChannelListController
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
We welcome code changes that improve this library or fix a problem,
|
||||||
|
please make sure to follow all best practices and add tests if applicable before submitting a Pull Request on Github.
|
||||||
|
We are pleased to merge your code into the official repository.
|
||||||
|
Make sure to sign our [Contributor License Agreement (CLA)](https://docs.google.com/forms/d/e/1FAIpQLScFKsKkAJI7mhCr7K9rEIOpqIDThrWxuvxnwUq2XkHyG154vQ/viewform) first.
|
||||||
|
See our license file for more details.
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
# Miscellaneous
|
||||||
|
*.class
|
||||||
|
*.log
|
||||||
|
*.pyc
|
||||||
|
*.swp
|
||||||
|
.DS_Store
|
||||||
|
.atom/
|
||||||
|
.buildlog/
|
||||||
|
.history
|
||||||
|
.svn/
|
||||||
|
|
||||||
|
# IntelliJ related
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# The .vscode folder contains launch configuration and tasks you configure in
|
||||||
|
# VS Code which you may wish to be included in version control, so this line
|
||||||
|
# is commented out by default.
|
||||||
|
#.vscode/
|
||||||
|
|
||||||
|
# Flutter/Dart/Pub related
|
||||||
|
**/doc/api/
|
||||||
|
**/ios/Flutter/.last_build_id
|
||||||
|
.dart_tool/
|
||||||
|
.flutter-plugins
|
||||||
|
.flutter-plugins-dependencies
|
||||||
|
.packages
|
||||||
|
.pub-cache/
|
||||||
|
.pub/
|
||||||
|
/build/
|
||||||
|
|
||||||
|
# Web related
|
||||||
|
lib/generated_plugin_registrant.dart
|
||||||
|
|
||||||
|
# Symbolication related
|
||||||
|
app.*.symbols
|
||||||
|
|
||||||
|
# Obfuscation related
|
||||||
|
app.*.map.json
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
# This file tracks properties of this Flutter project.
|
||||||
|
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||||
|
#
|
||||||
|
# This file should be version controlled and should not be manually edited.
|
||||||
|
|
||||||
|
version:
|
||||||
|
revision: 78910062997c3a836feee883712c241a5fd22983
|
||||||
|
channel: stable
|
||||||
|
|
||||||
|
project_type: app
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# example
|
||||||
|
|
||||||
|
Example app for testing stream_chat_flutter_core
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
This project is a starting point for a Flutter application.
|
||||||
|
|
||||||
|
A few resources to get you started if this is your first Flutter project:
|
||||||
|
|
||||||
|
- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
|
||||||
|
- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
|
||||||
|
|
||||||
|
For help getting started with Flutter, view our
|
||||||
|
[online documentation](https://flutter.dev/docs), which offers tutorials,
|
||||||
|
samples, guidance on mobile development, and a full API reference.
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
gradle-wrapper.jar
|
||||||
|
/.gradle
|
||||||
|
/captures/
|
||||||
|
/gradlew
|
||||||
|
/gradlew.bat
|
||||||
|
/local.properties
|
||||||
|
GeneratedPluginRegistrant.java
|
||||||
|
|
||||||
|
# Remember to never publicly share your keystore.
|
||||||
|
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
|
||||||
|
key.properties
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
def localProperties = new Properties()
|
||||||
|
def localPropertiesFile = rootProject.file('local.properties')
|
||||||
|
if (localPropertiesFile.exists()) {
|
||||||
|
localPropertiesFile.withReader('UTF-8') { reader ->
|
||||||
|
localProperties.load(reader)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def flutterRoot = localProperties.getProperty('flutter.sdk')
|
||||||
|
if (flutterRoot == null) {
|
||||||
|
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
|
||||||
|
}
|
||||||
|
|
||||||
|
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
|
||||||
|
if (flutterVersionCode == null) {
|
||||||
|
flutterVersionCode = '1'
|
||||||
|
}
|
||||||
|
|
||||||
|
def flutterVersionName = localProperties.getProperty('flutter.versionName')
|
||||||
|
if (flutterVersionName == null) {
|
||||||
|
flutterVersionName = '1.0'
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'com.android.application'
|
||||||
|
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion 29
|
||||||
|
|
||||||
|
lintOptions {
|
||||||
|
disable 'InvalidPackage'
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||||
|
applicationId "example.example"
|
||||||
|
minSdkVersion 16
|
||||||
|
targetSdkVersion 29
|
||||||
|
versionCode flutterVersionCode.toInteger()
|
||||||
|
versionName flutterVersionName
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
// TODO: Add your own signing config for the release build.
|
||||||
|
// Signing with the debug keys for now, so `flutter run --release` works.
|
||||||
|
signingConfig signingConfigs.debug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flutter {
|
||||||
|
source '../..'
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="example.example">
|
||||||
|
<!-- Flutter needs it to communicate with the running application
|
||||||
|
to allow setting breakpoints, to provide hot reload, etc.
|
||||||
|
-->
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
</manifest>
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="example.example">
|
||||||
|
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
|
||||||
|
calls FlutterMain.startInitialization(this); in its onCreate method.
|
||||||
|
In most cases you can leave this as-is, but you if you want to provide
|
||||||
|
additional functionality it is fine to subclass or reimplement
|
||||||
|
FlutterApplication and put your custom class here. -->
|
||||||
|
<application
|
||||||
|
android:name="io.flutter.app.FlutterApplication"
|
||||||
|
android:label="example"
|
||||||
|
android:icon="@mipmap/ic_launcher">
|
||||||
|
<activity
|
||||||
|
android:name=".MainActivity"
|
||||||
|
android:launchMode="singleTop"
|
||||||
|
android:theme="@style/LaunchTheme"
|
||||||
|
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||||
|
android:hardwareAccelerated="true"
|
||||||
|
android:windowSoftInputMode="adjustResize">
|
||||||
|
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||||
|
the Android process has started. This theme is visible to the user
|
||||||
|
while the Flutter UI initializes. After that, this theme continues
|
||||||
|
to determine the Window background behind the Flutter UI. -->
|
||||||
|
<meta-data
|
||||||
|
android:name="io.flutter.embedding.android.NormalTheme"
|
||||||
|
android:resource="@style/NormalTheme"
|
||||||
|
/>
|
||||||
|
<!-- Displays an Android View that continues showing the launch screen
|
||||||
|
Drawable until Flutter paints its first frame, then this splash
|
||||||
|
screen fades out. A splash screen is useful to avoid any visual
|
||||||
|
gap between the end of Android's launch screen and the painting of
|
||||||
|
Flutter's first frame. -->
|
||||||
|
<meta-data
|
||||||
|
android:name="io.flutter.embedding.android.SplashScreenDrawable"
|
||||||
|
android:resource="@drawable/launch_background"
|
||||||
|
/>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
<!-- Don't delete the meta-data below.
|
||||||
|
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||||
|
<meta-data
|
||||||
|
android:name="flutterEmbedding"
|
||||||
|
android:value="2" />
|
||||||
|
</application>
|
||||||
|
</manifest>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package example.example;
|
||||||
|
|
||||||
|
import io.flutter.embedding.android.FlutterActivity;
|
||||||
|
|
||||||
|
public class MainActivity extends FlutterActivity {}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Modify this file to customize your launch splash screen -->
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:drawable="@android:color/white" />
|
||||||
|
|
||||||
|
<!-- You can insert your own image assets here -->
|
||||||
|
<!-- <item>
|
||||||
|
<bitmap
|
||||||
|
android:gravity="center"
|
||||||
|
android:src="@mipmap/launch_image" />
|
||||||
|
</item> -->
|
||||||
|
</layer-list>
|
||||||
|
After Width: | Height: | Size: 544 B |
|
After Width: | Height: | Size: 442 B |
|
After Width: | Height: | Size: 721 B |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<!-- Theme applied to the Android Window while the process is starting -->
|
||||||
|
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||||
|
<!-- Show a splash screen on the activity. Automatically removed when
|
||||||
|
Flutter draws its first frame -->
|
||||||
|
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||||
|
</style>
|
||||||
|
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||||
|
This theme determines the color of the Android Window while your
|
||||||
|
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||||
|
running.
|
||||||
|
|
||||||
|
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||||
|
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||||
|
<item name="android:windowBackground">@android:color/white</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="example.example">
|
||||||
|
<!-- Flutter needs it to communicate with the running application
|
||||||
|
to allow setting breakpoints, to provide hot reload, etc.
|
||||||
|
-->
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
</manifest>
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
jcenter()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
classpath 'com.android.tools.build:gradle:3.5.0'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allprojects {
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
jcenter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rootProject.buildDir = '../build'
|
||||||
|
subprojects {
|
||||||
|
project.buildDir = "${rootProject.buildDir}/${project.name}"
|
||||||
|
}
|
||||||
|
subprojects {
|
||||||
|
project.evaluationDependsOn(':app')
|
||||||
|
}
|
||||||
|
|
||||||
|
task clean(type: Delete) {
|
||||||
|
delete rootProject.buildDir
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
org.gradle.jvmargs=-Xmx1536M
|
||||||
|
android.useAndroidX=true
|
||||||
|
android.enableJetifier=true
|
||||||
|
android.enableR8=true
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#Fri Jun 23 08:50:38 CEST 2017
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
include ':app'
|
||||||
|
|
||||||
|
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
|
||||||
|
def properties = new Properties()
|
||||||
|
|
||||||
|
assert localPropertiesFile.exists()
|
||||||
|
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
|
||||||
|
|
||||||
|
def flutterSdkPath = properties.getProperty("flutter.sdk")
|
||||||
|
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
|
||||||
|
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
*.mode1v3
|
||||||
|
*.mode2v3
|
||||||
|
*.moved-aside
|
||||||
|
*.pbxuser
|
||||||
|
*.perspectivev3
|
||||||
|
**/*sync/
|
||||||
|
.sconsign.dblite
|
||||||
|
.tags*
|
||||||
|
**/.vagrant/
|
||||||
|
**/DerivedData/
|
||||||
|
Icon?
|
||||||
|
**/Pods/
|
||||||
|
**/.symlinks/
|
||||||
|
profile
|
||||||
|
xcuserdata
|
||||||
|
**/.generated/
|
||||||
|
Flutter/App.framework
|
||||||
|
Flutter/Flutter.framework
|
||||||
|
Flutter/Flutter.podspec
|
||||||
|
Flutter/Generated.xcconfig
|
||||||
|
Flutter/app.flx
|
||||||
|
Flutter/app.zip
|
||||||
|
Flutter/flutter_assets/
|
||||||
|
Flutter/flutter_export_environment.sh
|
||||||
|
ServiceDefinitions.json
|
||||||
|
Runner/GeneratedPluginRegistrant.*
|
||||||
|
|
||||||
|
# Exceptions to above rules.
|
||||||
|
!default.mode1v3
|
||||||
|
!default.mode2v3
|
||||||
|
!default.pbxuser
|
||||||
|
!default.perspectivev3
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>App</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>io.flutter.flutter.app</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>App</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>FMWK</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>1.0</string>
|
||||||
|
<key>CFBundleSignature</key>
|
||||||
|
<string>????</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>1.0</string>
|
||||||
|
<key>MinimumOSVersion</key>
|
||||||
|
<string>8.0</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
|
||||||
|
#include "Generated.xcconfig"
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
|
||||||
|
#include "Generated.xcconfig"
|
||||||
@@ -0,0 +1,495 @@
|
|||||||
|
// !$*UTF8*$!
|
||||||
|
{
|
||||||
|
archiveVersion = 1;
|
||||||
|
classes = {
|
||||||
|
};
|
||||||
|
objectVersion = 46;
|
||||||
|
objects = {
|
||||||
|
|
||||||
|
/* Begin PBXBuildFile section */
|
||||||
|
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
|
||||||
|
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
||||||
|
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
|
||||||
|
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
||||||
|
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||||
|
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
||||||
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
|
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
|
||||||
|
isa = PBXCopyFilesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
dstPath = "";
|
||||||
|
dstSubfolderSpec = 10;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
name = "Embed Frameworks";
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXFileReference section */
|
||||||
|
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
||||||
|
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
||||||
|
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
||||||
|
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||||
|
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
|
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
||||||
|
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
|
||||||
|
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
|
||||||
|
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||||
|
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
|
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||||
|
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
97C146EB1CF9000F007C117D /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXGroup section */
|
||||||
|
9740EEB11CF90186004384FC /* Flutter */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
|
||||||
|
9740EEB21CF90195004384FC /* Debug.xcconfig */,
|
||||||
|
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
|
||||||
|
9740EEB31CF90195004384FC /* Generated.xcconfig */,
|
||||||
|
);
|
||||||
|
name = Flutter;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
97C146E51CF9000F007C117D = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
9740EEB11CF90186004384FC /* Flutter */,
|
||||||
|
97C146F01CF9000F007C117D /* Runner */,
|
||||||
|
97C146EF1CF9000F007C117D /* Products */,
|
||||||
|
);
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
97C146EF1CF9000F007C117D /* Products */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
97C146EE1CF9000F007C117D /* Runner.app */,
|
||||||
|
);
|
||||||
|
name = Products;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
97C146F01CF9000F007C117D /* Runner */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
97C146FA1CF9000F007C117D /* Main.storyboard */,
|
||||||
|
97C146FD1CF9000F007C117D /* Assets.xcassets */,
|
||||||
|
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
|
||||||
|
97C147021CF9000F007C117D /* Info.plist */,
|
||||||
|
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
|
||||||
|
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
|
||||||
|
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
|
||||||
|
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
|
||||||
|
);
|
||||||
|
path = Runner;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
/* End PBXGroup section */
|
||||||
|
|
||||||
|
/* Begin PBXNativeTarget section */
|
||||||
|
97C146ED1CF9000F007C117D /* Runner */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
|
||||||
|
buildPhases = (
|
||||||
|
9740EEB61CF901F6004384FC /* Run Script */,
|
||||||
|
97C146EA1CF9000F007C117D /* Sources */,
|
||||||
|
97C146EB1CF9000F007C117D /* Frameworks */,
|
||||||
|
97C146EC1CF9000F007C117D /* Resources */,
|
||||||
|
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
||||||
|
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
);
|
||||||
|
name = Runner;
|
||||||
|
productName = Runner;
|
||||||
|
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
|
||||||
|
productType = "com.apple.product-type.application";
|
||||||
|
};
|
||||||
|
/* End PBXNativeTarget section */
|
||||||
|
|
||||||
|
/* Begin PBXProject section */
|
||||||
|
97C146E61CF9000F007C117D /* Project object */ = {
|
||||||
|
isa = PBXProject;
|
||||||
|
attributes = {
|
||||||
|
LastUpgradeCheck = 1020;
|
||||||
|
ORGANIZATIONNAME = "";
|
||||||
|
TargetAttributes = {
|
||||||
|
97C146ED1CF9000F007C117D = {
|
||||||
|
CreatedOnToolsVersion = 7.3.1;
|
||||||
|
LastSwiftMigration = 1100;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
|
||||||
|
compatibilityVersion = "Xcode 9.3";
|
||||||
|
developmentRegion = en;
|
||||||
|
hasScannedForEncodings = 0;
|
||||||
|
knownRegions = (
|
||||||
|
en,
|
||||||
|
Base,
|
||||||
|
);
|
||||||
|
mainGroup = 97C146E51CF9000F007C117D;
|
||||||
|
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
|
||||||
|
projectDirPath = "";
|
||||||
|
projectRoot = "";
|
||||||
|
targets = (
|
||||||
|
97C146ED1CF9000F007C117D /* Runner */,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
/* End PBXProject section */
|
||||||
|
|
||||||
|
/* Begin PBXResourcesBuildPhase section */
|
||||||
|
97C146EC1CF9000F007C117D /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
|
||||||
|
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
|
||||||
|
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
|
||||||
|
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXShellScriptBuildPhase section */
|
||||||
|
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
);
|
||||||
|
name = "Thin Binary";
|
||||||
|
outputPaths = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
|
||||||
|
};
|
||||||
|
9740EEB61CF901F6004384FC /* Run Script */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
);
|
||||||
|
name = "Run Script";
|
||||||
|
outputPaths = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
|
||||||
|
};
|
||||||
|
/* End PBXShellScriptBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
|
97C146EA1CF9000F007C117D /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
|
||||||
|
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXVariantGroup section */
|
||||||
|
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
|
||||||
|
isa = PBXVariantGroup;
|
||||||
|
children = (
|
||||||
|
97C146FB1CF9000F007C117D /* Base */,
|
||||||
|
);
|
||||||
|
name = Main.storyboard;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
|
||||||
|
isa = PBXVariantGroup;
|
||||||
|
children = (
|
||||||
|
97C147001CF9000F007C117D /* Base */,
|
||||||
|
);
|
||||||
|
name = LaunchScreen.storyboard;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
/* End PBXVariantGroup section */
|
||||||
|
|
||||||
|
/* Begin XCBuildConfiguration section */
|
||||||
|
249021D3217E4FDB00AE95B9 /* Profile */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||||
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
|
ENABLE_NS_ASSERTIONS = NO;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
SUPPORTED_PLATFORMS = iphoneos;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
VALIDATE_PRODUCT = YES;
|
||||||
|
};
|
||||||
|
name = Profile;
|
||||||
|
};
|
||||||
|
249021D4217E4FDB00AE95B9 /* Profile */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
|
ENABLE_BITCODE = NO;
|
||||||
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"$(PROJECT_DIR)/Flutter",
|
||||||
|
);
|
||||||
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||||
|
LIBRARY_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"$(PROJECT_DIR)/Flutter",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = example.example;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
VERSIONING_SYSTEM = "apple-generic";
|
||||||
|
};
|
||||||
|
name = Profile;
|
||||||
|
};
|
||||||
|
97C147031CF9000F007C117D /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||||
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
ENABLE_TESTABILITY = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||||
|
GCC_DYNAMIC_NO_PIC = NO;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_OPTIMIZATION_LEVEL = 0;
|
||||||
|
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||||
|
"DEBUG=1",
|
||||||
|
"$(inherited)",
|
||||||
|
);
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = YES;
|
||||||
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
97C147041CF9000F007C117D /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||||
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
|
ENABLE_NS_ASSERTIONS = NO;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
SUPPORTED_PLATFORMS = iphoneos;
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
VALIDATE_PRODUCT = YES;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
97C147061CF9000F007C117D /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
|
ENABLE_BITCODE = NO;
|
||||||
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"$(PROJECT_DIR)/Flutter",
|
||||||
|
);
|
||||||
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||||
|
LIBRARY_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"$(PROJECT_DIR)/Flutter",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = example.example;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
VERSIONING_SYSTEM = "apple-generic";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
97C147071CF9000F007C117D /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
|
ENABLE_BITCODE = NO;
|
||||||
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"$(PROJECT_DIR)/Flutter",
|
||||||
|
);
|
||||||
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||||
|
LIBRARY_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"$(PROJECT_DIR)/Flutter",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = example.example;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
VERSIONING_SYSTEM = "apple-generic";
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
|
/* Begin XCConfigurationList section */
|
||||||
|
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
97C147031CF9000F007C117D /* Debug */,
|
||||||
|
97C147041CF9000F007C117D /* Release */,
|
||||||
|
249021D3217E4FDB00AE95B9 /* Profile */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
97C147061CF9000F007C117D /* Debug */,
|
||||||
|
97C147071CF9000F007C117D /* Release */,
|
||||||
|
249021D4217E4FDB00AE95B9 /* Profile */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
/* End XCConfigurationList section */
|
||||||
|
};
|
||||||
|
rootObject = 97C146E61CF9000F007C117D /* Project object */;
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Workspace
|
||||||
|
version = "1.0">
|
||||||
|
<FileRef
|
||||||
|
location = "group:Runner.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
|
</Workspace>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>IDEDidComputeMac32BitWarning</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>PreviewsEnabled</key>
|
||||||
|
<false/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Scheme
|
||||||
|
LastUpgradeVersion = "1020"
|
||||||
|
version = "1.3">
|
||||||
|
<BuildAction
|
||||||
|
parallelizeBuildables = "YES"
|
||||||
|
buildImplicitDependencies = "YES">
|
||||||
|
<BuildActionEntries>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||||
|
BuildableName = "Runner.app"
|
||||||
|
BlueprintName = "Runner"
|
||||||
|
ReferencedContainer = "container:Runner.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
</BuildActionEntries>
|
||||||
|
</BuildAction>
|
||||||
|
<TestAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
|
<Testables>
|
||||||
|
</Testables>
|
||||||
|
<MacroExpansion>
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||||
|
BuildableName = "Runner.app"
|
||||||
|
BlueprintName = "Runner"
|
||||||
|
ReferencedContainer = "container:Runner.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</MacroExpansion>
|
||||||
|
<AdditionalOptions>
|
||||||
|
</AdditionalOptions>
|
||||||
|
</TestAction>
|
||||||
|
<LaunchAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
launchStyle = "0"
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
debugDocumentVersioning = "YES"
|
||||||
|
debugServiceExtension = "internal"
|
||||||
|
allowLocationSimulation = "YES">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||||
|
BuildableName = "Runner.app"
|
||||||
|
BlueprintName = "Runner"
|
||||||
|
ReferencedContainer = "container:Runner.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
<AdditionalOptions>
|
||||||
|
</AdditionalOptions>
|
||||||
|
</LaunchAction>
|
||||||
|
<ProfileAction
|
||||||
|
buildConfiguration = "Profile"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
savedToolIdentifier = ""
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
debugDocumentVersioning = "YES">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||||
|
BuildableName = "Runner.app"
|
||||||
|
BlueprintName = "Runner"
|
||||||
|
ReferencedContainer = "container:Runner.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</ProfileAction>
|
||||||
|
<AnalyzeAction
|
||||||
|
buildConfiguration = "Debug">
|
||||||
|
</AnalyzeAction>
|
||||||
|
<ArchiveAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
revealArchiveInOrganizer = "YES">
|
||||||
|
</ArchiveAction>
|
||||||
|
</Scheme>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Workspace
|
||||||
|
version = "1.0">
|
||||||
|
<FileRef
|
||||||
|
location = "group:Runner.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
|
</Workspace>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>IDEDidComputeMac32BitWarning</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>PreviewsEnabled</key>
|
||||||
|
<false/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import UIKit
|
||||||
|
import Flutter
|
||||||
|
|
||||||
|
@UIApplicationMain
|
||||||
|
@objc class AppDelegate: FlutterAppDelegate {
|
||||||
|
override func application(
|
||||||
|
_ application: UIApplication,
|
||||||
|
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
||||||
|
) -> Bool {
|
||||||
|
GeneratedPluginRegistrant.register(with: self)
|
||||||
|
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"size" : "20x20",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "[email protected]",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "20x20",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "[email protected]",
|
||||||
|
"scale" : "3x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "29x29",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "[email protected]",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "29x29",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "[email protected]",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "29x29",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "[email protected]",
|
||||||
|
"scale" : "3x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "40x40",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "[email protected]",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "40x40",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "[email protected]",
|
||||||
|
"scale" : "3x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "60x60",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "[email protected]",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "60x60",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "[email protected]",
|
||||||
|
"scale" : "3x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "20x20",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "[email protected]",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "20x20",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "[email protected]",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "29x29",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "[email protected]",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "29x29",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "[email protected]",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "40x40",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "[email protected]",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "40x40",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "[email protected]",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "76x76",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "[email protected]",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "76x76",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "[email protected]",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "83.5x83.5",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "[email protected]",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "1024x1024",
|
||||||
|
"idiom" : "ios-marketing",
|
||||||
|
"filename" : "[email protected]",
|
||||||
|
"scale" : "1x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 564 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "LaunchImage.png",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "[email protected]",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "[email protected]",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 68 B |
|
After Width: | Height: | Size: 68 B |