flutter_chat_ui 0.6.5 flutter_chat_ui: ^0.6.5 copied to clipboard
Actively maintainable, community-driven chat UI implementation with an optional Firebase BaaS.
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart' show rootBundle;
import 'package:file_picker/file_picker.dart';
import 'package:flutter_chat_types/flutter_chat_types.dart' as types;
import 'package:flutter_chat_ui/flutter_chat_ui.dart';
import 'package:http/http.dart' as http;
import 'package:image_picker/image_picker.dart';
import 'package:intl/date_symbol_data_local.dart';
import 'package:mime/mime.dart';
import 'package:open_file/open_file.dart';
import 'package:path_provider/path_provider.dart';
import 'package:uuid/uuid.dart';
void main() {
initializeDateFormatting().then((_) => runApp(const MyApp()));
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Chat',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
// This makes the visual density adapt to the platform that you run
// the app on. For desktop platforms, the controls will be smaller and
// closer together (more dense) than on mobile platforms.
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: const ChatPage(),
);
}
}
class ChatPage extends StatefulWidget {
const ChatPage({Key? key}) : super(key: key);
@override
_ChatPageState createState() => _ChatPageState();
}
class _ChatPageState extends State<ChatPage> {
List<types.Message> _messages = [];
final _user = const types.User(id: '06c33e8b-e835-4736-80f4-63f44b66666c');
@override
void initState() {
super.initState();
_loadMessages();
}
void _addMessage(types.Message message) {
setState(() {
_messages.insert(0, message);
});
}
void _handleAtachmentPress() {
showModalBottomSheet<void>(
context: context,
builder: (BuildContext context) {
return SizedBox(
height: 180,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
TextButton(
onPressed: () {
Navigator.pop(context);
_showFilePicker();
},
child: const Align(
alignment: Alignment.centerLeft,
child: Text('Open file picker'),
),
),
TextButton(
onPressed: () {
Navigator.pop(context);
_showImagePicker();
},
child: const Align(
alignment: Alignment.centerLeft,
child: Text('Open image picker'),
),
),
TextButton(
onPressed: () => Navigator.pop(context),
child: const Align(
alignment: Alignment.centerLeft,
child: Text('Cancel'),
),
),
],
),
);
},
);
}
void _loadMessages() async {
final response = await rootBundle.loadString('assets/messages.json');
final messages = (jsonDecode(response) as List)
.map((e) => types.Message.fromJson(e as Map<String, dynamic>))
.toList();
setState(() {
_messages = messages;
});
}
void _onPreviewDataFetched(
types.TextMessage message,
types.PreviewData previewData,
) {
final index = _messages.indexWhere((element) => element.id == message.id);
final currentMessage = _messages[index] as types.TextMessage;
final updatedMessage = currentMessage.copyWith(previewData);
WidgetsBinding.instance?.addPostFrameCallback((_) {
setState(() {
_messages[index] = updatedMessage;
});
});
}
void _onSendMessage(types.PartialText message) {
final textMessage = types.TextMessage(
authorId: _user.id,
id: const Uuid().v4(),
text: message.text,
timestamp: (DateTime.now().millisecondsSinceEpoch / 1000).floor(),
);
_addMessage(textMessage);
}
void _openFile(types.FileMessage message) async {
var localPath = message.uri;
if (message.uri.startsWith('http')) {
final client = http.Client();
final request = await client.get(Uri.parse(message.uri));
final bytes = request.bodyBytes;
final documentsDir = (await getApplicationDocumentsDirectory()).path;
localPath = '$documentsDir/${message.fileName}';
if (!File(localPath).existsSync()) {
final file = File(localPath);
await file.writeAsBytes(bytes);
}
}
await OpenFile.open(localPath);
}
void _showFilePicker() async {
final result = await FilePicker.platform.pickFiles(
type: FileType.any,
);
if (result != null) {
final message = types.FileMessage(
authorId: _user.id,
fileName: result.files.single.name ?? '',
id: const Uuid().v4(),
mimeType: lookupMimeType(result.files.single.path ?? ''),
size: result.files.single.size ?? 0,
timestamp: (DateTime.now().millisecondsSinceEpoch / 1000).floor(),
uri: result.files.single.path ?? '',
);
_addMessage(message);
} else {
// User canceled the picker
}
}
void _showImagePicker() async {
final result = await ImagePicker().getImage(
imageQuality: 70,
maxWidth: 1440,
source: ImageSource.gallery,
);
if (result != null) {
final size = File(result.path).lengthSync();
final bytes = await result.readAsBytes();
final image = await decodeImageFromList(bytes);
final imageName = result.path.split('/').last;
final message = types.ImageMessage(
authorId: _user.id,
height: image.height.toDouble(),
id: const Uuid().v4(),
imageName: imageName,
size: size,
timestamp: (DateTime.now().millisecondsSinceEpoch / 1000).floor(),
uri: result.path,
width: image.width.toDouble(),
);
_addMessage(message);
} else {
// User canceled the picker
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Chat(
messages: _messages,
onAttachmentPressed: _handleAtachmentPress,
onFilePressed: _openFile,
onPreviewDataFetched: _onPreviewDataFetched,
onSendPressed: _onSendMessage,
user: _user,
),
);
}
}