241 lines
7.6 KiB
Dart
241 lines
7.6 KiB
Dart
import 'package:matrix/matrix.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:provider/provider.dart';
|
|
|
|
void main() {
|
|
runApp(FamedlySdkExampleApp());
|
|
}
|
|
|
|
class FamedlySdkExampleApp extends StatelessWidget {
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Provider<Client>(
|
|
create: (_) => Client('Famedly SDK Example App'),
|
|
child: Builder(
|
|
builder: (context) => MaterialApp(
|
|
title: 'Famedly SDK Example App',
|
|
home: StreamBuilder<LoginState>(
|
|
stream: Provider.of<Client>(context).onLoginStateChanged.stream,
|
|
builder:
|
|
(BuildContext context, AsyncSnapshot<LoginState> snapshot) {
|
|
if (snapshot.hasError) {
|
|
return Center(child: Text(snapshot.error.toString()));
|
|
}
|
|
if (snapshot.data == LoginState.loggedIn) {
|
|
return ChatListView();
|
|
}
|
|
return LoginView();
|
|
},
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class LoginView extends StatefulWidget {
|
|
@override
|
|
_LoginViewState createState() => _LoginViewState();
|
|
}
|
|
|
|
class _LoginViewState extends State<LoginView> {
|
|
final TextEditingController _usernameController = TextEditingController(),
|
|
_passwordController = TextEditingController(),
|
|
_domainController = TextEditingController();
|
|
|
|
String _errorText;
|
|
|
|
bool _isLoading = false;
|
|
|
|
void _loginAction(Client client) async {
|
|
setState(() {
|
|
_errorText = null;
|
|
_isLoading = true;
|
|
});
|
|
try {
|
|
await client.checkHomeserver(_domainController.text);
|
|
await client.login(
|
|
user: _usernameController.text,
|
|
password: _passwordController.text,
|
|
);
|
|
} catch (e) {
|
|
setState(() => _errorText = e.toString());
|
|
}
|
|
setState(() => _isLoading = false);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final client = Provider.of<Client>(context);
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
title: Text('Famedly SDK Example App'),
|
|
),
|
|
body: ListView(
|
|
padding: EdgeInsets.all(16),
|
|
children: [
|
|
TextField(
|
|
controller: _usernameController,
|
|
readOnly: _isLoading,
|
|
autocorrect: false,
|
|
decoration: InputDecoration(
|
|
border: OutlineInputBorder(),
|
|
labelText: 'Username',
|
|
),
|
|
),
|
|
SizedBox(height: 16),
|
|
TextField(
|
|
controller: _passwordController,
|
|
readOnly: _isLoading,
|
|
autocorrect: false,
|
|
obscureText: true,
|
|
decoration: InputDecoration(
|
|
border: OutlineInputBorder(),
|
|
labelText: 'Password',
|
|
),
|
|
),
|
|
SizedBox(height: 16),
|
|
TextField(
|
|
controller: _domainController,
|
|
readOnly: _isLoading,
|
|
autocorrect: false,
|
|
decoration: InputDecoration(
|
|
border: OutlineInputBorder(),
|
|
labelText: 'Password',
|
|
hintText: 'https://matrix.org',
|
|
errorText: _errorText,
|
|
errorMaxLines: 4,
|
|
),
|
|
),
|
|
SizedBox(height: 16),
|
|
RaisedButton(
|
|
child: _isLoading ? LinearProgressIndicator() : Text('Login'),
|
|
onPressed: _isLoading ? null : () => _loginAction(client),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class ChatListView extends StatelessWidget {
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final client = Provider.of<Client>(context);
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
title: Text('Chats'),
|
|
),
|
|
body: StreamBuilder(
|
|
stream: client.onSync.stream,
|
|
builder: (context, _) => ListView.builder(
|
|
itemCount: client.rooms.length,
|
|
itemBuilder: (BuildContext context, int i) => ListTile(
|
|
leading: CircleAvatar(
|
|
backgroundImage: client.rooms[i].avatar == null
|
|
? null
|
|
: NetworkImage(
|
|
client.rooms[i].avatar.getThumbnail(
|
|
client,
|
|
width: 64,
|
|
height: 64,
|
|
),
|
|
),
|
|
),
|
|
title: Text(client.rooms[i].displayname),
|
|
subtitle: Text(client.rooms[i].lastMessage),
|
|
onTap: () => Navigator.of(context).push(
|
|
MaterialPageRoute(
|
|
builder: (_) => ChatView(roomId: client.rooms[i].id),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class ChatView extends StatelessWidget {
|
|
final String roomId;
|
|
|
|
const ChatView({Key key, @required this.roomId}) : super(key: key);
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final client = Provider.of<Client>(context);
|
|
final TextEditingController _sendController = TextEditingController();
|
|
return StreamBuilder<Object>(
|
|
stream: client.onSync.stream,
|
|
builder: (context, _) {
|
|
final room = client.getRoomById(roomId);
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
title: Text(room.displayname),
|
|
),
|
|
body: SafeArea(
|
|
child: FutureBuilder<Timeline>(
|
|
future: room.getTimeline(),
|
|
builder:
|
|
(BuildContext context, AsyncSnapshot<Timeline> snapshot) {
|
|
if (!snapshot.hasData) {
|
|
return Center(child: CircularProgressIndicator());
|
|
}
|
|
final timeline = snapshot.data;
|
|
return Column(
|
|
children: [
|
|
Expanded(
|
|
child: ListView.builder(
|
|
reverse: true,
|
|
itemCount: timeline.events.length,
|
|
itemBuilder: (BuildContext context, int i) {
|
|
final event = timeline.events[i];
|
|
final sender = event.sender;
|
|
return ListTile(
|
|
leading: CircleAvatar(
|
|
backgroundImage: sender.avatarUrl == null
|
|
? null
|
|
: NetworkImage(
|
|
sender.avatarUrl.getThumbnail(
|
|
client,
|
|
width: 64,
|
|
height: 64,
|
|
),
|
|
),
|
|
),
|
|
title: Text(sender.calcDisplayname()),
|
|
subtitle: Text(event.body),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
Divider(height: 1),
|
|
Container(
|
|
height: 56,
|
|
child: Row(
|
|
children: [
|
|
Expanded(
|
|
child: TextField(
|
|
controller: _sendController,
|
|
),
|
|
),
|
|
IconButton(
|
|
icon: Icon(Icons.send),
|
|
onPressed: () {
|
|
room.sendTextEvent(_sendController.text);
|
|
_sendController.clear();
|
|
},
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
),
|
|
),
|
|
);
|
|
});
|
|
}
|
|
}
|