feat: Added download and sharing of images
This commit is contained in:
@@ -6,6 +6,7 @@
|
|||||||
additional functionality it is fine to subclass or reimplement
|
additional functionality it is fine to subclass or reimplement
|
||||||
FlutterApplication and put your custom class here. -->
|
FlutterApplication and put your custom class here. -->
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||||
<application
|
<application
|
||||||
android:name=".Application"
|
android:name=".Application"
|
||||||
android:label="ChatSample"
|
android:label="ChatSample"
|
||||||
|
|||||||
@@ -112,6 +112,8 @@ class _FullScreenImageState extends State<FullScreenImage>
|
|||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
message: widget.message,
|
message: widget.message,
|
||||||
|
urls: widget.urls,
|
||||||
|
currentIndex: _currentPage,
|
||||||
),
|
),
|
||||||
ImageFooter(
|
ImageFooter(
|
||||||
currentPage: _currentPage,
|
currentPage: _currentPage,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gallery_saver/gallery_saver.dart';
|
||||||
|
|
||||||
import '../stream_chat_flutter.dart';
|
import '../stream_chat_flutter.dart';
|
||||||
import 'stream_icons.dart';
|
import 'stream_icons.dart';
|
||||||
@@ -9,8 +10,11 @@ class ImageActionsModal extends StatelessWidget {
|
|||||||
final Message message;
|
final Message message;
|
||||||
final String userName;
|
final String userName;
|
||||||
final String sentAt;
|
final String sentAt;
|
||||||
|
final List<String> urls;
|
||||||
|
final currentIndex;
|
||||||
|
|
||||||
ImageActionsModal({this.message, this.userName, this.sentAt});
|
ImageActionsModal(
|
||||||
|
{this.message, this.userName, this.sentAt, this.urls, this.currentIndex});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -112,8 +116,12 @@ class ImageActionsModal extends StatelessWidget {
|
|||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
}),
|
}),
|
||||||
_buildButton(
|
_buildButton(context, 'Save Image', StreamIcons.save_1,
|
||||||
context, 'Save Image', StreamIcons.save_1, () {}),
|
() async {
|
||||||
|
await GallerySaver.saveImage(
|
||||||
|
urls[currentIndex].split('?')[0]);
|
||||||
|
Navigator.pop(context);
|
||||||
|
}),
|
||||||
_buildButton(context, 'Copy', StreamIcons.copy, () {}),
|
_buildButton(context, 'Copy', StreamIcons.copy, () {}),
|
||||||
if (StreamChat.of(context).user.id == message.user.id)
|
if (StreamChat.of(context).user.id == message.user.id)
|
||||||
_buildButton(context, 'Delete', StreamIcons.delete,
|
_buildButton(context, 'Delete', StreamIcons.delete,
|
||||||
|
|||||||
+49
-15
@@ -1,7 +1,12 @@
|
|||||||
|
import 'dart:io';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
|
import 'package:esys_flutter_share/esys_flutter_share.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gallery_saver/gallery_saver.dart';
|
||||||
import 'package:stream_chat/stream_chat.dart';
|
import 'package:stream_chat/stream_chat.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';
|
||||||
@@ -61,6 +66,7 @@ class _ImageFooterState extends State<ImageFooter> {
|
|||||||
TextEditingController _messageController = TextEditingController();
|
TextEditingController _messageController = TextEditingController();
|
||||||
|
|
||||||
List<User> _selectedUsers = [];
|
List<User> _selectedUsers = [];
|
||||||
|
bool _loading = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -311,7 +317,8 @@ class _ImageFooterState extends State<ImageFooter> {
|
|||||||
_selectedUsers
|
_selectedUsers
|
||||||
.remove(user);
|
.remove(user);
|
||||||
} else {
|
} else {
|
||||||
_selectedUsers.add(user);
|
_selectedUsers
|
||||||
|
.add(user);
|
||||||
}
|
}
|
||||||
modalSetState(() {});
|
modalSetState(() {});
|
||||||
},
|
},
|
||||||
@@ -368,14 +375,23 @@ class _ImageFooterState extends State<ImageFooter> {
|
|||||||
margin: EdgeInsets.symmetric(
|
margin: EdgeInsets.symmetric(
|
||||||
horizontal: 8.0,
|
horizontal: 8.0,
|
||||||
),
|
),
|
||||||
child: SizedBox.expand(
|
child: Material(
|
||||||
child: Center(
|
child: InkWell(
|
||||||
child: Text(
|
onTap: () async {
|
||||||
'Save to Photos',
|
await GallerySaver.saveImage(
|
||||||
style: TextStyle(
|
widget.urls[widget.currentPage].split('?')[0]);
|
||||||
color: StreamChatTheme.of(modalContext)
|
Navigator.pop(context);
|
||||||
.accentColor,
|
},
|
||||||
fontWeight: FontWeight.bold,
|
child: SizedBox.expand(
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
'Save to Photos',
|
||||||
|
style: TextStyle(
|
||||||
|
color: StreamChatTheme.of(modalContext)
|
||||||
|
.accentColor,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -493,7 +509,14 @@ class _ImageFooterState extends State<ImageFooter> {
|
|||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
),
|
),
|
||||||
iconSize: 24.0,
|
iconSize: 24.0,
|
||||||
onPressed: () {},
|
onPressed: () async {
|
||||||
|
var url = widget.urls[widget.currentPage];
|
||||||
|
var type = url?.split('?')?.first?.split('.')?.last ?? 'jpg';
|
||||||
|
var request = await HttpClient().getUrl(Uri.parse(url));
|
||||||
|
var response = await request.close();
|
||||||
|
var bytes = await consolidateHttpClientResponseBytes(response);
|
||||||
|
await Share.file('File', 'image.$type', bytes, 'image/$type');
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -507,7 +530,12 @@ class _ImageFooterState extends State<ImageFooter> {
|
|||||||
child: Container(
|
child: Container(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
height: 56.0,
|
height: 56.0,
|
||||||
child: Row(
|
child: _loading ? Center(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
),
|
||||||
|
) : Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
@@ -553,8 +581,14 @@ class _ImageFooterState extends State<ImageFooter> {
|
|||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () {
|
onTap: () async {
|
||||||
sendMessage();
|
modalSetState(() {
|
||||||
|
_loading = true;
|
||||||
|
});
|
||||||
|
await sendMessage();
|
||||||
|
modalSetState(() {
|
||||||
|
_loading = false;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
child: Transform.rotate(
|
child: Transform.rotate(
|
||||||
angle: -pi / 2,
|
angle: -pi / 2,
|
||||||
@@ -607,7 +641,7 @@ class _ImageFooterState extends State<ImageFooter> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Sends the current message
|
/// Sends the current message
|
||||||
void sendMessage() async {
|
Future sendMessage() async {
|
||||||
var text = _messageController.text.trim();
|
var text = _messageController.text.trim();
|
||||||
|
|
||||||
final attachments = widget.message.attachments;
|
final attachments = widget.message.attachments;
|
||||||
@@ -616,7 +650,7 @@ class _ImageFooterState extends State<ImageFooter> {
|
|||||||
|
|
||||||
final client = StreamChat.of(context).client;
|
final client = StreamChat.of(context).client;
|
||||||
|
|
||||||
for(var user in _selectedUsers) {
|
for (var user in _selectedUsers) {
|
||||||
var c = client.channel('messaging', extraData: {
|
var c = client.channel('messaging', extraData: {
|
||||||
'members': [
|
'members': [
|
||||||
user.id,
|
user.id,
|
||||||
|
|||||||
@@ -30,10 +30,15 @@ class ImageHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
final String userName;
|
final String userName;
|
||||||
final String sentAt;
|
final String sentAt;
|
||||||
|
|
||||||
|
final List<String> urls;
|
||||||
|
final currentIndex;
|
||||||
|
|
||||||
/// Creates a channel header
|
/// Creates a channel header
|
||||||
ImageHeader({
|
ImageHeader({
|
||||||
Key key,
|
Key key,
|
||||||
this.message,
|
this.message,
|
||||||
|
this.urls,
|
||||||
|
this.currentIndex,
|
||||||
this.showBackButton = true,
|
this.showBackButton = true,
|
||||||
this.onBackPressed,
|
this.onBackPressed,
|
||||||
this.onTitleTap,
|
this.onTitleTap,
|
||||||
@@ -117,6 +122,8 @@ class ImageHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
userName: userName,
|
userName: userName,
|
||||||
sentAt: sentAt,
|
sentAt: sentAt,
|
||||||
message: message,
|
message: message,
|
||||||
|
urls: urls,
|
||||||
|
currentIndex: currentIndex,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ dependencies:
|
|||||||
flutter_slidable: ^0.5.4
|
flutter_slidable: ^0.5.4
|
||||||
carousel_slider: ^2.2.1
|
carousel_slider: ^2.2.1
|
||||||
clipboard: ^0.1.2+8
|
clipboard: ^0.1.2+8
|
||||||
|
gallery_saver: ^2.0.1
|
||||||
|
esys_flutter_share: ^1.0.2
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
assets:
|
assets:
|
||||||
|
|||||||
Reference in New Issue
Block a user