265 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			Dart
		
	
	
	
			
		
		
	
	
			265 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			Dart
		
	
	
	
| import 'package:famedlysdk/famedlysdk.dart';
 | |
| import 'package:flutter/material.dart';
 | |
| 
 | |
| void main() {
 | |
|   runApp(FamedlySdkExampleApp());
 | |
| }
 | |
| 
 | |
| class FamedlySdkExampleApp extends StatelessWidget {
 | |
|   static Client client = Client('Famedly SDK Example Client', debug: true);
 | |
|   @override
 | |
|   Widget build(BuildContext context) {
 | |
|     return MaterialApp(
 | |
|       title: 'Famedly SDK Example App',
 | |
|       home: LoginView(),
 | |
|     );
 | |
|   }
 | |
| }
 | |
| 
 | |
| class LoginView extends StatefulWidget {
 | |
|   @override
 | |
|   _LoginViewState createState() => _LoginViewState();
 | |
| }
 | |
| 
 | |
| class _LoginViewState extends State<LoginView> {
 | |
|   final TextEditingController _homeserverController = TextEditingController();
 | |
|   final TextEditingController _usernameController = TextEditingController();
 | |
|   final TextEditingController _passwordController = TextEditingController();
 | |
|   bool _isLoading = false;
 | |
|   String _error;
 | |
| 
 | |
|   void _loginAction() async {
 | |
|     setState(() => _isLoading = true);
 | |
|     setState(() => _error = null);
 | |
|     try {
 | |
|       if (await FamedlySdkExampleApp.client
 | |
|               .checkServer(_homeserverController.text) ==
 | |
|           false) {
 | |
|         throw (Exception('Server not supported'));
 | |
|       }
 | |
|       if (await FamedlySdkExampleApp.client.login(
 | |
|             _usernameController.text,
 | |
|             _passwordController.text,
 | |
|           ) ==
 | |
|           false) {
 | |
|         throw (Exception('Username or password incorrect'));
 | |
|       }
 | |
|       Navigator.of(context).pushAndRemoveUntil(
 | |
|         MaterialPageRoute(builder: (_) => ChatListView()),
 | |
|         (route) => false,
 | |
|       );
 | |
|     } catch (e) {
 | |
|       setState(() => _error = e.toString());
 | |
|     }
 | |
|     setState(() => _isLoading = false);
 | |
|   }
 | |
| 
 | |
|   @override
 | |
|   Widget build(BuildContext context) {
 | |
|     return Scaffold(
 | |
|       appBar: AppBar(title: Text('Login')),
 | |
|       body: ListView(
 | |
|         padding: const EdgeInsets.all(16),
 | |
|         children: [
 | |
|           TextField(
 | |
|             controller: _homeserverController,
 | |
|             readOnly: _isLoading,
 | |
|             autocorrect: false,
 | |
|             decoration: InputDecoration(
 | |
|               labelText: 'Homeserver',
 | |
|               hintText: 'https://matrix.org',
 | |
|             ),
 | |
|           ),
 | |
|           SizedBox(height: 8),
 | |
|           TextField(
 | |
|             controller: _usernameController,
 | |
|             readOnly: _isLoading,
 | |
|             autocorrect: false,
 | |
|             decoration: InputDecoration(
 | |
|               labelText: 'Username',
 | |
|               hintText: '@username:domain',
 | |
|             ),
 | |
|           ),
 | |
|           SizedBox(height: 8),
 | |
|           TextField(
 | |
|             controller: _passwordController,
 | |
|             obscureText: true,
 | |
|             readOnly: _isLoading,
 | |
|             autocorrect: false,
 | |
|             decoration: InputDecoration(
 | |
|               labelText: 'Password',
 | |
|               hintText: '****',
 | |
|               errorText: _error,
 | |
|             ),
 | |
|           ),
 | |
|           SizedBox(height: 8),
 | |
|           RaisedButton(
 | |
|             child: _isLoading ? LinearProgressIndicator() : Text('Login'),
 | |
|             onPressed: _isLoading ? null : _loginAction,
 | |
|           ),
 | |
|         ],
 | |
|       ),
 | |
|     );
 | |
|   }
 | |
| }
 | |
| 
 | |
| class ChatListView extends StatefulWidget {
 | |
|   @override
 | |
|   _ChatListViewState createState() => _ChatListViewState();
 | |
| }
 | |
| 
 | |
| class _ChatListViewState extends State<ChatListView> {
 | |
|   @override
 | |
|   Widget build(BuildContext context) {
 | |
|     return Scaffold(
 | |
|       appBar: AppBar(
 | |
|         title: Text('Chats'),
 | |
|       ),
 | |
|       body: StreamBuilder(
 | |
|         stream: FamedlySdkExampleApp.client.onSync.stream,
 | |
|         builder: (c, s) => ListView.builder(
 | |
|           itemCount: FamedlySdkExampleApp.client.rooms.length,
 | |
|           itemBuilder: (BuildContext context, int i) {
 | |
|             final room = FamedlySdkExampleApp.client.rooms[i];
 | |
|             return ListTile(
 | |
|               title: Text(room.displayname + ' (${room.notificationCount})'),
 | |
|               subtitle: Text(room.lastMessage, maxLines: 1),
 | |
|               leading: CircleAvatar(
 | |
|                 backgroundImage: NetworkImage(room.avatar.getThumbnail(
 | |
|                   FamedlySdkExampleApp.client,
 | |
|                   width: 64,
 | |
|                   height: 64,
 | |
|                 )),
 | |
|               ),
 | |
|               onTap: () => Navigator.of(context).push(
 | |
|                 MaterialPageRoute(
 | |
|                   builder: (_) => ChatView(room: room),
 | |
|                 ),
 | |
|               ),
 | |
|             );
 | |
|           },
 | |
|         ),
 | |
|       ),
 | |
|     );
 | |
|   }
 | |
| }
 | |
| 
 | |
| class ChatView extends StatefulWidget {
 | |
|   final Room room;
 | |
| 
 | |
|   const ChatView({Key key, @required this.room}) : super(key: key);
 | |
| 
 | |
|   @override
 | |
|   _ChatViewState createState() => _ChatViewState();
 | |
| }
 | |
| 
 | |
| class _ChatViewState extends State<ChatView> {
 | |
|   final TextEditingController _controller = TextEditingController();
 | |
| 
 | |
|   void _sendAction() {
 | |
|     print('Send Text');
 | |
|     widget.room.sendTextEvent(_controller.text);
 | |
|     _controller.clear();
 | |
|   }
 | |
| 
 | |
|   Timeline timeline;
 | |
| 
 | |
|   Future<bool> getTimeline() async {
 | |
|     timeline ??=
 | |
|         await widget.room.getTimeline(onUpdate: () => setState(() => null));
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   @override
 | |
|   void dispose() {
 | |
|     timeline?.cancelSubscriptions();
 | |
|     super.dispose();
 | |
|   }
 | |
| 
 | |
|   @override
 | |
|   Widget build(BuildContext context) {
 | |
|     return Scaffold(
 | |
|       appBar: AppBar(
 | |
|         title: StreamBuilder<Object>(
 | |
|             stream: widget.room.onUpdate.stream,
 | |
|             builder: (context, snapshot) {
 | |
|               return Text(widget.room.displayname);
 | |
|             }),
 | |
|       ),
 | |
|       body: Column(
 | |
|         children: [
 | |
|           Expanded(
 | |
|             child: FutureBuilder(
 | |
|               future: getTimeline(),
 | |
|               builder: (context, snapshot) => !snapshot.hasData
 | |
|                   ? Center(
 | |
|                       child: CircularProgressIndicator(),
 | |
|                     )
 | |
|                   : ListView.builder(
 | |
|                       reverse: true,
 | |
|                       itemCount: timeline.events.length,
 | |
|                       itemBuilder: (BuildContext context, int i) => Opacity(
 | |
|                         opacity: timeline.events[i].status != 2 ? 0.5 : 1,
 | |
|                         child: ListTile(
 | |
|                           title: Row(
 | |
|                             children: [
 | |
|                               Expanded(
 | |
|                                 child: Text(
 | |
|                                   timeline.events[i].sender.calcDisplayname(),
 | |
|                                 ),
 | |
|                               ),
 | |
|                               Text(
 | |
|                                 timeline.events[i].originServerTs
 | |
|                                     .toIso8601String(),
 | |
|                                 style: TextStyle(fontSize: 12),
 | |
|                               ),
 | |
|                             ],
 | |
|                           ),
 | |
|                           subtitle: Text(timeline.events[i].body),
 | |
|                           leading: CircleAvatar(
 | |
|                             child: timeline.events[i].sender?.avatarUrl == null
 | |
|                                 ? Icon(Icons.person)
 | |
|                                 : null,
 | |
|                             backgroundImage:
 | |
|                                 timeline.events[i].sender?.avatarUrl != null
 | |
|                                     ? NetworkImage(
 | |
|                                         timeline.events[i].sender?.avatarUrl
 | |
|                                             ?.getThumbnail(
 | |
|                                           FamedlySdkExampleApp.client,
 | |
|                                           width: 64,
 | |
|                                           height: 64,
 | |
|                                         ),
 | |
|                                       )
 | |
|                                     : null,
 | |
|                           ),
 | |
|                         ),
 | |
|                       ),
 | |
|                     ),
 | |
|             ),
 | |
|           ),
 | |
|           Container(
 | |
|             height: 60,
 | |
|             child: Row(
 | |
|               children: [
 | |
|                 Expanded(
 | |
|                   child: TextField(
 | |
|                     controller: _controller,
 | |
|                     decoration: InputDecoration(
 | |
|                       contentPadding: const EdgeInsets.symmetric(horizontal: 8),
 | |
|                       labelText: 'Send a message ...',
 | |
|                     ),
 | |
|                   ),
 | |
|                 ),
 | |
|                 IconButton(
 | |
|                   icon: Icon(Icons.send),
 | |
|                   onPressed: _sendAction,
 | |
|                 )
 | |
|               ],
 | |
|             ),
 | |
|           ),
 | |
|         ],
 | |
|       ),
 | |
|     );
 | |
|   }
 | |
| }
 |