Soru/moor

This commit is contained in:
Sorunome 2020-05-13 13:58:59 +00:00 committed by Christian Pauly
commit f594c7005d
55 changed files with 1034 additions and 1133 deletions

View file

@ -1,4 +1,3 @@
import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/components/adaptive_page_layout.dart';
import 'package:fluffychat/components/matrix.dart';
import 'package:fluffychat/l10n/l10n.dart';
@ -21,7 +20,7 @@ class AppInfoView extends StatelessWidget {
class AppInfo extends StatelessWidget {
@override
Widget build(BuildContext context) {
Client client = Matrix.of(context).client;
var client = Matrix.of(context).client;
return Scaffold(
appBar: AppBar(
title: Text(L10n.of(context).accountInformations),
@ -29,43 +28,39 @@ class AppInfo extends StatelessWidget {
body: ListView(
children: <Widget>[
ListTile(
title: Text(L10n.of(context).yourOwnUsername + ":"),
title: Text(L10n.of(context).yourOwnUsername + ':'),
subtitle: Text(client.userID),
),
ListTile(
title: Text("Homeserver:"),
title: Text('Homeserver:'),
subtitle: Text(client.homeserver),
),
ListTile(
title: Text("Supported versions:"),
subtitle: Text(client.matrixVersions.toString()),
),
ListTile(
title: Text("Device name:"),
title: Text('Device name:'),
subtitle: Text(client.deviceName),
),
ListTile(
title: Text("Device ID:"),
title: Text('Device ID:'),
subtitle: Text(client.deviceID),
),
ListTile(
title: Text("Encryption enabled:"),
title: Text('Encryption enabled:'),
subtitle: Text(client.encryptionEnabled.toString()),
),
if (client.encryptionEnabled)
Column(
children: <Widget>[
ListTile(
title: Text("Your public fingerprint key:"),
title: Text('Your public fingerprint key:'),
subtitle: Text(client.fingerprintKey.beautified),
),
ListTile(
title: Text("Your public identity key:"),
title: Text('Your public identity key:'),
subtitle: Text(client.identityKey.beautified),
),
ListTile(
title: Text("LibOlm version:"),
subtitle: Text(olm.get_library_version().join(".")),
title: Text('LibOlm version:'),
subtitle: Text(olm.get_library_version().join('.')),
),
],
),

View file

@ -44,7 +44,7 @@ class _ArchiveState extends State<Archive> {
),
secondScaffold: Scaffold(
body: Center(
child: Image.asset("assets/logo.png", width: 100, height: 100),
child: Image.asset('assets/logo.png', width: 100, height: 100),
),
),
primaryPage: FocusPage.FIRST,

View file

@ -14,8 +14,9 @@ class AuthWebView extends StatelessWidget {
@override
Widget build(BuildContext context) {
final String url = Matrix.of(context).client.homeserver +
"/_matrix/client/r0/auth/$authType/fallback/web?session=$session";
final url =
'/_matrix/client/r0/auth/$authType/fallback/web?session=$session' +
Matrix.of(context).client.homeserver;
if (kIsWeb) launch(url);
return Scaffold(
appBar: AppBar(

View file

@ -55,7 +55,7 @@ class _ChatState extends State<_Chat> {
MatrixState matrix;
String seenByText = "";
String seenByText = '';
final ScrollController _scrollController = ScrollController();
@ -77,15 +77,19 @@ class _ChatState extends State<_Chat> {
final int _loadHistoryCount = 100;
String inputText = "";
String inputText = '';
bool get _canLoadMore => timeline.events.last.type != EventTypes.RoomCreate;
void requestHistory() async {
if (_canLoadMore) {
setState(() => this._loadingHistory = true);
await timeline.requestHistory(historyCount: _loadHistoryCount);
if (mounted) setState(() => this._loadingHistory = false);
setState(() => _loadingHistory = true);
try {
await timeline.requestHistory(historyCount: _loadHistoryCount);
} catch (e) {
debugPrint('Error loading history: ' + e.toString());
}
if (mounted) setState(() => _loadingHistory = false);
}
}
@ -114,9 +118,9 @@ class _ChatState extends State<_Chat> {
void updateView() {
if (!mounted) return;
String seenByText = "";
var seenByText = '';
if (timeline.events.isNotEmpty) {
List lastReceipts = List.from(timeline.events.first.receipts);
var lastReceipts = List.from(timeline.events.first.receipts);
lastReceipts.removeWhere((r) =>
r.user.id == room.client.userID ||
r.user.id == timeline.events.first.senderId);
@ -147,7 +151,7 @@ class _ChatState extends State<_Chat> {
unawaited(room.sendReadReceipt(timeline.events.first.eventId));
}
if (timeline.events.length < _loadHistoryCount) {
this.requestHistory();
requestHistory();
}
}
updateView();
@ -158,7 +162,7 @@ class _ChatState extends State<_Chat> {
void dispose() {
timeline?.cancelSubscriptions();
timeline = null;
matrix.activeRoomId = "";
matrix.activeRoomId = '';
super.dispose();
}
@ -167,12 +171,12 @@ class _ChatState extends State<_Chat> {
void send() {
if (sendController.text.isEmpty) return;
room.sendTextEvent(sendController.text, inReplyTo: replyEvent);
sendController.text = "";
sendController.text = '';
if (replyEvent != null) {
setState(() => replyEvent = null);
}
setState(() => inputText = "");
setState(() => inputText = '');
}
void sendFileAction(BuildContext context) async {
@ -180,7 +184,7 @@ class _ChatState extends State<_Chat> {
BotToast.showText(text: L10n.of(context).notSupportedInWeb);
return;
}
File file = await FilePicker.getFile();
var file = await FilePicker.getFile();
if (file == null) return;
await SimpleDialogs(context).tryRequestWithLoadingDialog(
room.sendFileEvent(
@ -194,7 +198,7 @@ class _ChatState extends State<_Chat> {
BotToast.showText(text: L10n.of(context).notSupportedInWeb);
return;
}
File file = await ImagePicker.pickImage(
var file = await ImagePicker.pickImage(
source: ImageSource.gallery,
imageQuality: 50,
maxWidth: 1600,
@ -212,7 +216,7 @@ class _ChatState extends State<_Chat> {
BotToast.showText(text: L10n.of(context).notSupportedInWeb);
return;
}
File file = await ImagePicker.pickImage(
var file = await ImagePicker.pickImage(
source: ImageSource.camera,
imageQuality: 50,
maxWidth: 1600,
@ -233,7 +237,7 @@ class _ChatState extends State<_Chat> {
onFinished: (r) => result = r,
));
if (result == null) return;
final File audioFile = File(result);
final audioFile = File(result);
await SimpleDialogs(context).tryRequestWithLoadingDialog(
room.sendAudioEvent(
MatrixFile(bytes: audioFile.readAsBytesSync(), path: audioFile.path),
@ -242,12 +246,12 @@ class _ChatState extends State<_Chat> {
}
String _getSelectedEventString(BuildContext context) {
String copyString = "";
var copyString = '';
if (selectedEvents.length == 1) {
return selectedEvents.first.getLocalizedBody(L10n.of(context));
}
for (Event event in selectedEvents) {
if (copyString.isNotEmpty) copyString += "\n\n";
for (var event in selectedEvents) {
if (copyString.isNotEmpty) copyString += '\n\n';
copyString +=
event.getLocalizedBody(L10n.of(context), withSenderNamePrefix: true);
}
@ -260,12 +264,12 @@ class _ChatState extends State<_Chat> {
}
void redactEventsAction(BuildContext context) async {
bool confirmed = await SimpleDialogs(context).askConfirmation(
var confirmed = await SimpleDialogs(context).askConfirmation(
titleText: L10n.of(context).messageWillBeRemovedWarning,
confirmText: L10n.of(context).remove,
);
if (!confirmed) return;
for (Event event in selectedEvents) {
for (var event in selectedEvents) {
await SimpleDialogs(context).tryRequestWithLoadingDialog(
event.status > 0 ? event.redact() : event.remove());
}
@ -273,7 +277,7 @@ class _ChatState extends State<_Chat> {
}
bool get canRedactSelectedEvents {
for (Event event in selectedEvents) {
for (var event in selectedEvents) {
if (event.canRedact == false) return false;
}
return true;
@ -284,8 +288,8 @@ class _ChatState extends State<_Chat> {
Matrix.of(context).shareContent = selectedEvents.first.content;
} else {
Matrix.of(context).shareContent = {
"msgtype": "m.text",
"body": _getSelectedEventString(context),
'msgtype': 'm.text',
'body': _getSelectedEventString(context),
};
}
setState(() => selectedEvents.clear());
@ -308,7 +312,7 @@ class _ChatState extends State<_Chat> {
@override
Widget build(BuildContext context) {
matrix = Matrix.of(context);
Client client = matrix.client;
var client = matrix.client;
room ??= client.getRoomById(widget.id);
if (room == null) {
return Scaffold(
@ -326,8 +330,8 @@ class _ChatState extends State<_Chat> {
SimpleDialogs(context).tryRequestWithLoadingDialog(room.join());
}
String typingText = "";
List<User> typingUsers = room.typingUsers;
var typingText = '';
var typingUsers = room.typingUsers;
typingUsers.removeWhere((User u) => u.id == client.userID);
if (typingUsers.length == 1) {
@ -616,22 +620,22 @@ class _ChatState extends State<_Chat> {
PopupMenuButton<String>(
icon: Icon(Icons.add),
onSelected: (String choice) async {
if (choice == "file") {
if (choice == 'file') {
sendFileAction(context);
} else if (choice == "image") {
} else if (choice == 'image') {
sendImageAction(context);
}
if (choice == "camera") {
if (choice == 'camera') {
openCameraAction(context);
}
if (choice == "voice") {
if (choice == 'voice') {
voiceMessageAction(context);
}
},
itemBuilder: (BuildContext context) =>
<PopupMenuEntry<String>>[
PopupMenuItem<String>(
value: "file",
value: 'file',
child: ListTile(
leading: CircleAvatar(
backgroundColor: Colors.green,
@ -644,7 +648,7 @@ class _ChatState extends State<_Chat> {
),
),
PopupMenuItem<String>(
value: "image",
value: 'image',
child: ListTile(
leading: CircleAvatar(
backgroundColor: Colors.blue,
@ -657,7 +661,7 @@ class _ChatState extends State<_Chat> {
),
),
PopupMenuItem<String>(
value: "camera",
value: 'camera',
child: ListTile(
leading: CircleAvatar(
backgroundColor: Colors.purple,
@ -670,7 +674,7 @@ class _ChatState extends State<_Chat> {
),
),
PopupMenuItem<String>(
value: "voice",
value: 'voice',
child: ListTile(
leading: CircleAvatar(
backgroundColor: Colors.red,
@ -708,20 +712,20 @@ class _ChatState extends State<_Chat> {
border: InputBorder.none,
),
onChanged: (String text) {
this.typingCoolDown?.cancel();
this.typingCoolDown =
typingCoolDown?.cancel();
typingCoolDown =
Timer(Duration(seconds: 2), () {
this.typingCoolDown = null;
this.currentlyTyping = false;
typingCoolDown = null;
currentlyTyping = false;
room.sendTypingInfo(false);
});
this.typingTimeout ??=
typingTimeout ??=
Timer(Duration(seconds: 30), () {
this.typingTimeout = null;
this.currentlyTyping = false;
typingTimeout = null;
currentlyTyping = false;
});
if (!this.currentlyTyping) {
this.currentlyTyping = true;
if (!currentlyTyping) {
currentlyTyping = true;
room.sendTypingInfo(true,
timeout: Duration(seconds: 30)
.inMilliseconds);

View file

@ -1,5 +1,3 @@
import 'dart:io';
import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/components/adaptive_page_layout.dart';
import 'package:fluffychat/components/chat_settings_popup_menu.dart';
@ -30,11 +28,12 @@ class ChatDetails extends StatefulWidget {
class _ChatDetailsState extends State<ChatDetails> {
List<User> members;
void setDisplaynameAction(BuildContext context) async {
final String displayname = await SimpleDialogs(context).enterText(
var enterText = SimpleDialogs(context).enterText(
titleText: L10n.of(context).changeTheNameOfTheGroup,
labelText: L10n.of(context).changeTheNameOfTheGroup,
hintText: widget.room.getLocalizedDisplayname(L10n.of(context)),
);
final displayname = await enterText;
if (displayname == null) return;
final success = await SimpleDialogs(context).tryRequestWithLoadingDialog(
widget.room.setName(displayname),
@ -45,26 +44,26 @@ class _ChatDetailsState extends State<ChatDetails> {
}
void setCanonicalAliasAction(context) async {
final String s = await SimpleDialogs(context).enterText(
final s = await SimpleDialogs(context).enterText(
titleText: L10n.of(context).setInvitationLink,
labelText: L10n.of(context).setInvitationLink,
hintText: L10n.of(context).alias.toLowerCase(),
prefixText: "#",
suffixText: ":" + widget.room.client.userID.domain,
prefixText: '#',
suffixText: ':' + widget.room.client.userID.domain,
);
if (s == null) return;
final String domain = widget.room.client.userID.domain;
final String canonicalAlias = "%23" + s + "%3A" + domain;
final Event aliasEvent = widget.room.getState("m.room.aliases", domain);
final List aliases =
aliasEvent != null ? aliasEvent.content["aliases"] ?? [] : [];
final domain = widget.room.client.userID.domain;
final canonicalAlias = '%23' + s + '%3A' + domain;
final aliasEvent = widget.room.getState('m.room.aliases', domain);
final aliases =
aliasEvent != null ? aliasEvent.content['aliases'] ?? [] : [];
if (aliases.indexWhere((s) => s == canonicalAlias) == -1) {
List<String> newAliases = List.from(aliases);
var newAliases = List<String>.from(aliases);
newAliases.add(canonicalAlias);
final response = await SimpleDialogs(context).tryRequestWithLoadingDialog(
widget.room.client.jsonRequest(
type: HTTPType.GET,
action: "/client/r0/directory/room/$canonicalAlias",
action: '/client/r0/directory/room/$canonicalAlias',
),
);
if (response == false) {
@ -72,8 +71,8 @@ class _ChatDetailsState extends State<ChatDetails> {
await SimpleDialogs(context).tryRequestWithLoadingDialog(
widget.room.client.jsonRequest(
type: HTTPType.PUT,
action: "/client/r0/directory/room/$canonicalAlias",
data: {"room_id": widget.room.id}),
action: '/client/r0/directory/room/$canonicalAlias',
data: {'room_id': widget.room.id}),
);
if (success == false) return;
}
@ -82,13 +81,13 @@ class _ChatDetailsState extends State<ChatDetails> {
widget.room.client.jsonRequest(
type: HTTPType.PUT,
action:
"/client/r0/rooms/${widget.room.id}/state/m.room.canonical_alias",
data: {"alias": "#$s:$domain"}),
'/client/r0/rooms/${widget.room.id}/state/m.room.canonical_alias',
data: {'alias': '#$s:$domain'}),
);
}
void setTopicAction(BuildContext context) async {
final String displayname = await SimpleDialogs(context).enterText(
final displayname = await SimpleDialogs(context).enterText(
titleText: L10n.of(context).setGroupDescription,
labelText: L10n.of(context).setGroupDescription,
hintText: (widget.room.topic?.isNotEmpty ?? false)
@ -106,7 +105,7 @@ class _ChatDetailsState extends State<ChatDetails> {
}
void setAvatarAction(BuildContext context) async {
final File tempFile = await ImagePicker.pickImage(
final tempFile = await ImagePicker.pickImage(
source: ImageSource.gallery,
imageQuality: 50,
maxWidth: 1600,
@ -145,9 +144,9 @@ class _ChatDetailsState extends State<ChatDetails> {
}
members ??= widget.room.getParticipants();
members.removeWhere((u) => u.membership == Membership.leave);
final int actualMembersCount =
final actualMembersCount =
widget.room.mInvitedMemberCount + widget.room.mJoinedMemberCount;
final bool canRequestMoreMembers = members.length < actualMembersCount;
final canRequestMoreMembers = members.length < actualMembersCount;
return AdaptivePageLayout(
primaryPage: FocusPage.SECOND,
firstScaffold: ChatList(
@ -189,7 +188,7 @@ class _ChatDetailsState extends State<ChatDetails> {
backgroundColor: Theme.of(context).appBarTheme.color,
flexibleSpace: FlexibleSpaceBar(
background: ContentBanner(widget.room.avatar,
onEdit: widget.room.canSendEvent("m.room.avatar") &&
onEdit: widget.room.canSendEvent('m.room.avatar') &&
!kIsWeb
? () => setAvatarAction(context)
: null),
@ -204,7 +203,7 @@ class _ChatDetailsState extends State<ChatDetails> {
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
ListTile(
leading: widget.room.canSendEvent("m.room.topic")
leading: widget.room.canSendEvent('m.room.topic')
? CircleAvatar(
backgroundColor: Theme.of(context)
.scaffoldBackgroundColor,
@ -213,7 +212,7 @@ class _ChatDetailsState extends State<ChatDetails> {
)
: null,
title: Text(
"${L10n.of(context).groupDescription}:",
'${L10n.of(context).groupDescription}:',
style: TextStyle(
color: Theme.of(context).primaryColor,
fontWeight: FontWeight.bold)),
@ -230,7 +229,7 @@ class _ChatDetailsState extends State<ChatDetails> {
.color,
),
),
onTap: widget.room.canSendEvent("m.room.topic")
onTap: widget.room.canSendEvent('m.room.topic')
? () => setTopicAction(context)
: null,
),
@ -244,7 +243,7 @@ class _ChatDetailsState extends State<ChatDetails> {
),
),
),
if (widget.room.canSendEvent("m.room.name"))
if (widget.room.canSendEvent('m.room.name'))
ListTile(
leading: CircleAvatar(
backgroundColor:
@ -259,7 +258,7 @@ class _ChatDetailsState extends State<ChatDetails> {
onTap: () => setDisplaynameAction(context),
),
if (widget.room
.canSendEvent("m.room.canonical_alias") &&
.canSendEvent('m.room.canonical_alias') &&
widget.room.joinRules == JoinRules.public)
ListTile(
leading: CircleAvatar(

View file

@ -50,14 +50,14 @@ class _ChatEncryptionSettingsState extends State<ChatEncryptionSettings> {
if (snapshot.hasError) {
return Center(
child: Text(L10n.of(context).oopsSomethingWentWrong +
": " +
': ' +
snapshot.error.toString()),
);
}
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
}
final List<DeviceKeys> deviceKeys = snapshot.data;
final deviceKeys = snapshot.data;
return ListView.separated(
separatorBuilder: (BuildContext context, int i) =>
Divider(height: 1),
@ -96,7 +96,7 @@ class _ChatEncryptionSettingsState extends State<ChatEncryptionSettings> {
),
subtitle: Text(
deviceKeys[i]
.keys["ed25519:${deviceKeys[i].deviceId}"]
.keys['ed25519:${deviceKeys[i].deviceId}']
.beautified,
style: TextStyle(
color:

View file

@ -35,7 +35,7 @@ class ChatListView extends StatelessWidget {
firstScaffold: ChatList(),
secondScaffold: Scaffold(
body: Center(
child: Image.asset("assets/logo.png", width: 100, height: 100),
child: Image.asset('assets/logo.png', width: 100, height: 100),
),
),
);
@ -62,7 +62,7 @@ class _ChatListState extends State<ChatList> {
final ScrollController _scrollController = ScrollController();
Future<void> waitForFirstSync(BuildContext context) async {
Client client = Matrix.of(context).client;
var client = Matrix.of(context).client;
if (client.prevBatch?.isEmpty ?? true) {
await client.onFirstSync.stream.first;
}
@ -106,7 +106,7 @@ class _ChatListState extends State<ChatList> {
publicRoomsResponse = newPublicRoomsResponse;
if (searchController.text.isNotEmpty &&
searchController.text.isValidMatrixId &&
searchController.text.sigil == "#") {
searchController.text.sigil == '#') {
publicRoomsResponse.publicRooms.add(
PublicRoomEntry(
aliases: [searchController.text],
@ -134,11 +134,11 @@ class _ChatListState extends State<ChatList> {
if (Navigator.of(context).canPop()) {
Navigator.of(context).popUntil((r) => r.isFirst);
}
final File file = File(files.first.path);
final file = File(files.first.path);
Matrix.of(context).shareContent = {
"msgtype": "chat.fluffy.shared_file",
"file": MatrixFile(
'msgtype': 'chat.fluffy.shared_file',
'file': MatrixFile(
bytes: file.readAsBytesSync(),
path: file.path,
),
@ -150,13 +150,13 @@ class _ChatListState extends State<ChatList> {
if (Navigator.of(context).canPop()) {
Navigator.of(context).popUntil((r) => r.isFirst);
}
if (text.startsWith("https://matrix.to/#/")) {
if (text.startsWith('https://matrix.to/#/')) {
UrlLauncher(context, text).openMatrixToUrl();
return;
}
Matrix.of(context).shareContent = {
"msgtype": "m.text",
"body": text,
'msgtype': 'm.text',
'body': text,
};
}
@ -204,8 +204,8 @@ class _ChatListState extends State<ChatList> {
action:
'/client/r0/presence/${Matrix.of(context).client.userID}/status',
data: {
"presence": "online",
"status_msg": status,
'presence': 'online',
'status_msg': status,
},
),
);
@ -288,7 +288,7 @@ class _ChatListState extends State<ChatList> {
Navigator.of(context).pop();
Share.share(L10n.of(context).inviteText(
Matrix.of(context).client.userID,
"https://matrix.to/#/${Matrix.of(context).client.userID}"));
'https://matrix.to/#/${Matrix.of(context).client.userID}'));
},
),
],
@ -381,13 +381,13 @@ class _ChatListState extends State<ChatList> {
future: waitForFirstSync(context),
builder: (BuildContext context, snapshot) {
if (snapshot.hasData) {
List<Room> rooms = List<Room>.from(
var rooms = List<Room>.from(
Matrix.of(context).client.rooms);
rooms.removeWhere((Room room) =>
searchMode &&
!room.displayname.toLowerCase().contains(
searchController.text.toLowerCase() ??
""));
''));
if (rooms.isEmpty &&
(!searchMode ||
publicRoomsResponse == null)) {
@ -410,10 +410,10 @@ class _ChatListState extends State<ChatList> {
),
);
}
final int publicRoomsCount =
final publicRoomsCount =
(publicRoomsResponse?.publicRooms?.length ??
0);
final int totalCount =
final totalCount =
rooms.length + publicRoomsCount;
return ListView.separated(
controller: _scrollController,

View file

@ -8,7 +8,7 @@ import 'package:fluffychat/views/sign_up.dart';
import 'package:flutter/material.dart';
class HomeserverPicker extends StatelessWidget {
_setHomeserverAction(BuildContext context) async {
Future<void> _setHomeserverAction(BuildContext context) async {
final homeserver = await SimpleDialogs(context).enterText(
titleText: L10n.of(context).enterYourHomeserver,
hintText: Matrix.defaultHomeserver,
@ -17,7 +17,7 @@ class HomeserverPicker extends StatelessWidget {
_checkHomeserverAction(homeserver, context);
}
_checkHomeserverAction(String homeserver, BuildContext context) async {
void _checkHomeserverAction(String homeserver, BuildContext context) async {
if (!homeserver.startsWith('https://')) {
homeserver = 'https://$homeserver';
}
@ -40,7 +40,7 @@ class HomeserverPicker extends StatelessWidget {
children: <Widget>[
Hero(
tag: 'loginBanner',
child: Image.asset("assets/fluffychat-banner.png"),
child: Image.asset('assets/fluffychat-banner.png'),
),
Padding(
padding: const EdgeInsets.all(16.0),

View file

@ -27,17 +27,18 @@ class _InvitationSelectionState extends State<InvitationSelection> {
Timer coolDown;
Future<List<User>> getContacts(BuildContext context) async {
final Client client = Matrix.of(context).client;
List<User> participants = await widget.room.requestParticipants();
var client2 = Matrix.of(context).client;
final client = client2;
var participants = await widget.room.requestParticipants();
participants.removeWhere(
(u) => ![Membership.join, Membership.invite].contains(u.membership),
);
List<User> contacts = [];
Map<String, bool> userMap = {};
for (int i = 0; i < client.rooms.length; i++) {
List<User> roomUsers = client.rooms[i].getParticipants();
var contacts = <User>[];
var userMap = <String, bool>{};
for (var i = 0; i < client.rooms.length; i++) {
var roomUsers = client.rooms[i].getParticipants();
for (int j = 0; j < roomUsers.length; j++) {
for (var j = 0; j < roomUsers.length; j++) {
if (userMap[roomUsers[j].id] != true &&
participants.indexWhere((u) => u.id == roomUsers[j].id) == -1) {
contacts.add(roomUsers[j]);
@ -81,41 +82,41 @@ class _InvitationSelectionState extends State<InvitationSelection> {
if (currentSearchTerm.isEmpty) return;
if (loading) return;
setState(() => loading = true);
final MatrixState matrix = Matrix.of(context);
final matrix = Matrix.of(context);
final response = await SimpleDialogs(context).tryRequestWithErrorToast(
matrix.client.jsonRequest(
type: HTTPType.POST,
action: "/client/r0/user_directory/search",
action: '/client/r0/user_directory/search',
data: {
"search_term": text,
"limit": 10,
'search_term': text,
'limit': 10,
}),
);
setState(() => loading = false);
if (response == false ||
!(response is Map) ||
(response["results"] == null)) return;
(response['results'] == null)) return;
setState(() {
foundProfiles = List<Map<String, dynamic>>.from(response["results"]);
if ("@$text".isValidMatrixId &&
foundProfiles = List<Map<String, dynamic>>.from(response['results']);
if ('@$text'.isValidMatrixId &&
foundProfiles
.indexWhere((profile) => "@$text" == profile["user_id"]) ==
.indexWhere((profile) => '@$text' == profile['user_id']) ==
-1) {
setState(() => foundProfiles = [
{"user_id": "@$text"}
{'user_id': '@$text'}
]);
}
foundProfiles.removeWhere((profile) =>
widget.room
.getParticipants()
.indexWhere((u) => u.id == profile["user_id"]) !=
.indexWhere((u) => u.id == profile['user_id']) !=
-1);
});
}
@override
Widget build(BuildContext context) {
final String groupName = widget.room.name?.isEmpty ?? false
final groupName = widget.room.name?.isEmpty ?? false
? L10n.of(context).group
: widget.room.name;
return AdaptivePageLayout(
@ -138,7 +139,7 @@ class _InvitationSelectionState extends State<InvitationSelection> {
onSubmitted: (String text) => searchUser(context, text),
decoration: InputDecoration(
border: OutlineInputBorder(),
prefixText: "@",
prefixText: '@',
hintText: L10n.of(context).username,
labelText: L10n.of(context).inviteContactToGroup(groupName),
suffixIcon: loading
@ -159,19 +160,19 @@ class _InvitationSelectionState extends State<InvitationSelection> {
itemCount: foundProfiles.length,
itemBuilder: (BuildContext context, int i) => ListTile(
leading: Avatar(
foundProfiles[i]["avatar_url"] == null
foundProfiles[i]['avatar_url'] == null
? null
: Uri.parse(foundProfiles[i]["avatar_url"]),
foundProfiles[i]["display_name"] ??
foundProfiles[i]["user_id"],
: Uri.parse(foundProfiles[i]['avatar_url']),
foundProfiles[i]['display_name'] ??
foundProfiles[i]['user_id'],
),
title: Text(
foundProfiles[i]["display_name"] ??
(foundProfiles[i]["user_id"] as String).localpart,
foundProfiles[i]['display_name'] ??
(foundProfiles[i]['user_id'] as String).localpart,
),
subtitle: Text(foundProfiles[i]["user_id"]),
subtitle: Text(foundProfiles[i]['user_id']),
onTap: () =>
inviteAction(context, foundProfiles[i]["user_id"]),
inviteAction(context, foundProfiles[i]['user_id']),
),
)
: FutureBuilder<List<User>>(
@ -182,7 +183,7 @@ class _InvitationSelectionState extends State<InvitationSelection> {
child: CircularProgressIndicator(),
);
}
List<User> contacts = snapshot.data;
var contacts = snapshot.data;
return ListView.builder(
itemCount: contacts.length,
itemBuilder: (BuildContext context, int i) => ListTile(

View file

@ -24,7 +24,7 @@ class _LoginState extends State<Login> {
bool showPassword = false;
void login(BuildContext context) async {
MatrixState matrix = Matrix.of(context);
var matrix = Matrix.of(context);
if (usernameController.text.isEmpty) {
setState(() => usernameError = L10n.of(context).pleaseEnterYourUsername);
} else {
@ -101,7 +101,7 @@ class _LoginState extends State<Login> {
controller: usernameController,
decoration: InputDecoration(
hintText:
"@${L10n.of(context).username.toLowerCase()}:domain",
'@${L10n.of(context).username.toLowerCase()}:domain',
errorText: usernameError,
labelText: L10n.of(context).username),
),
@ -120,7 +120,7 @@ class _LoginState extends State<Login> {
obscureText: !showPassword,
onSubmitted: (t) => login(context),
decoration: InputDecoration(
hintText: "****",
hintText: '****',
errorText: passwordError,
suffixIcon: IconButton(
icon: Icon(showPassword

View file

@ -31,18 +31,18 @@ class _NewGroupState extends State<_NewGroup> {
bool publicGroup = false;
void submitAction(BuildContext context) async {
final MatrixState matrix = Matrix.of(context);
Map<String, dynamic> params = {};
final matrix = Matrix.of(context);
var params = <String, dynamic>{};
if (publicGroup) {
params["preset"] = "public_chat";
params["visibility"] = "public";
params['preset'] = 'public_chat';
params['visibility'] = 'public';
if (controller.text.isNotEmpty) {
params["room_alias_name"] = controller.text;
params['room_alias_name'] = controller.text;
}
} else {
params["preset"] = "private_chat";
params['preset'] = 'private_chat';
}
if (controller.text.isNotEmpty) params["name"] = controller.text;
if (controller.text.isNotEmpty) params['name'] = controller.text;
final String roomID =
await SimpleDialogs(context).tryRequestWithLoadingDialog(
matrix.client.createRoom(params: params),
@ -99,7 +99,7 @@ class _NewGroupState extends State<_NewGroup> {
onChanged: (bool b) => setState(() => publicGroup = b),
),
Expanded(
child: Image.asset("assets/new_group_wallpaper.png"),
child: Image.asset('assets/new_group_wallpaper.png'),
),
],
),

View file

@ -37,23 +37,23 @@ class _NewPrivateChatState extends State<_NewPrivateChat> {
List<Map<String, dynamic>> foundProfiles = [];
Timer coolDown;
Map<String, dynamic> get foundProfile => foundProfiles.firstWhere(
(user) => user["user_id"] == "@$currentSearchTerm",
(user) => user['user_id'] == '@$currentSearchTerm',
orElse: () => null);
bool get correctMxId =>
foundProfiles
.indexWhere((user) => user["user_id"] == "@$currentSearchTerm") !=
.indexWhere((user) => user['user_id'] == '@$currentSearchTerm') !=
-1;
void submitAction(BuildContext context) async {
if (controller.text.isEmpty) return;
if (!_formKey.currentState.validate()) return;
final MatrixState matrix = Matrix.of(context);
final matrix = Matrix.of(context);
if ("@" + controller.text.trim() == matrix.client.userID) return;
if ('@' + controller.text.trim() == matrix.client.userID) return;
final User user = User(
"@" + controller.text.trim(),
room: Room(id: "", client: matrix.client),
final user = User(
'@' + controller.text.trim(),
room: Room(id: '', client: matrix.client),
);
final String roomID = await SimpleDialogs(context)
.tryRequestWithLoadingDialog(user.startDirectChat());
@ -87,22 +87,22 @@ class _NewPrivateChatState extends State<_NewPrivateChat> {
if (currentSearchTerm.isEmpty) return;
if (loading) return;
setState(() => loading = true);
final MatrixState matrix = Matrix.of(context);
final matrix = Matrix.of(context);
final response = await SimpleDialogs(context).tryRequestWithErrorToast(
matrix.client.jsonRequest(
type: HTTPType.POST,
action: "/client/r0/user_directory/search",
action: '/client/r0/user_directory/search',
data: {
"search_term": text,
"limit": 10,
'search_term': text,
'limit': 10,
}),
);
setState(() => loading = false);
if (response == false ||
!(response is Map) ||
(response["results"]?.isEmpty ?? true)) return;
(response['results']?.isEmpty ?? true)) return;
setState(() {
foundProfiles = List<Map<String, dynamic>>.from(response["results"]);
foundProfiles = List<Map<String, dynamic>>.from(response['results']);
});
}
@ -131,15 +131,15 @@ class _NewPrivateChatState extends State<_NewPrivateChat> {
if (value.isEmpty) {
return L10n.of(context).pleaseEnterAMatrixIdentifier;
}
final MatrixState matrix = Matrix.of(context);
String mxid = "@" + controller.text.trim();
final matrix = Matrix.of(context);
var mxid = '@' + controller.text.trim();
if (mxid == matrix.client.userID) {
return L10n.of(context).youCannotInviteYourself;
}
if (!mxid.contains("@")) {
if (!mxid.contains('@')) {
return L10n.of(context).makeSureTheIdentifierIsValid;
}
if (!mxid.contains(":")) {
if (!mxid.contains(':')) {
return L10n.of(context).makeSureTheIdentifierIsValid;
}
return null;
@ -158,17 +158,17 @@ class _NewPrivateChatState extends State<_NewPrivateChat> {
? Padding(
padding: const EdgeInsets.all(8.0),
child: Avatar(
foundProfile["avatar_url"] == null
foundProfile['avatar_url'] == null
? null
: Uri.parse(foundProfile["avatar_url"]),
foundProfile["display_name"] ??
foundProfile["user_id"],
: Uri.parse(foundProfile['avatar_url']),
foundProfile['display_name'] ??
foundProfile['user_id'],
size: 12,
),
)
: Icon(Icons.account_circle),
prefixText: "@",
hintText: "${L10n.of(context).username.toLowerCase()}",
prefixText: '@',
hintText: '${L10n.of(context).username.toLowerCase()}',
),
),
),
@ -179,29 +179,29 @@ class _NewPrivateChatState extends State<_NewPrivateChat> {
child: ListView.builder(
itemCount: foundProfiles.length,
itemBuilder: (BuildContext context, int i) {
Map<String, dynamic> foundProfile = foundProfiles[i];
var foundProfile = foundProfiles[i];
return ListTile(
onTap: () {
setState(() {
controller.text = currentSearchTerm =
foundProfile["user_id"].substring(1);
foundProfile['user_id'].substring(1);
});
},
leading: Avatar(
foundProfile["avatar_url"] == null
foundProfile['avatar_url'] == null
? null
: Uri.parse(foundProfile["avatar_url"]),
foundProfile["display_name"] ?? foundProfile["user_id"],
: Uri.parse(foundProfile['avatar_url']),
foundProfile['display_name'] ?? foundProfile['user_id'],
//size: 24,
),
title: Text(
foundProfile["display_name"] ??
(foundProfile["user_id"] as String).localpart,
foundProfile['display_name'] ??
(foundProfile['user_id'] as String).localpart,
style: TextStyle(),
maxLines: 1,
),
subtitle: Text(
foundProfile["user_id"],
foundProfile['user_id'],
maxLines: 1,
style: TextStyle(
fontSize: 12,
@ -219,9 +219,9 @@ class _NewPrivateChatState extends State<_NewPrivateChat> {
),
onTap: () => Share.share(L10n.of(context).inviteText(
Matrix.of(context).client.userID,
"https://matrix.to/#/${Matrix.of(context).client.userID}")),
'https://matrix.to/#/${Matrix.of(context).client.userID}')),
title: Text(
"${L10n.of(context).yourOwnUsername}:",
'${L10n.of(context).yourOwnUsername}:',
style: TextStyle(
fontStyle: FontStyle.italic,
),
@ -237,7 +237,7 @@ class _NewPrivateChatState extends State<_NewPrivateChat> {
Divider(height: 1),
if (foundProfiles.isEmpty || correctMxId)
Expanded(
child: Image.asset("assets/private_chat_wallpaper.png"),
child: Image.asset('assets/private_chat_wallpaper.png'),
),
],
),

View file

@ -1,5 +1,3 @@
import 'dart:io';
import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/components/settings_themes.dart';
import 'package:fluffychat/views/settings_devices.dart';
@ -42,7 +40,7 @@ class _SettingsState extends State<Settings> {
if (await SimpleDialogs(context).askConfirmation() == false) {
return;
}
MatrixState matrix = Matrix.of(context);
var matrix = Matrix.of(context);
await SimpleDialogs(context)
.tryRequestWithLoadingDialog(matrix.client.logout());
}
@ -57,20 +55,20 @@ class _SettingsState extends State<Settings> {
if (!jitsi.endsWith('/')) {
jitsi += '/';
}
final MatrixState matrix = Matrix.of(context);
await matrix.client.storeAPI.setItem('chat.fluffy.jitsi_instance', jitsi);
final matrix = Matrix.of(context);
await matrix.store.setItem('chat.fluffy.jitsi_instance', jitsi);
matrix.jitsiInstance = jitsi;
}
void setDisplaynameAction(BuildContext context) async {
final String displayname = await SimpleDialogs(context).enterText(
final displayname = await SimpleDialogs(context).enterText(
titleText: L10n.of(context).editDisplayname,
hintText:
profile?.displayname ?? Matrix.of(context).client.userID.localpart,
labelText: L10n.of(context).enterAUsername,
);
if (displayname == null) return;
final MatrixState matrix = Matrix.of(context);
final matrix = Matrix.of(context);
final success = await SimpleDialogs(context).tryRequestWithLoadingDialog(
matrix.client.setDisplayname(displayname),
);
@ -83,13 +81,13 @@ class _SettingsState extends State<Settings> {
}
void setAvatarAction(BuildContext context) async {
final File tempFile = await ImagePicker.pickImage(
final tempFile = await ImagePicker.pickImage(
source: ImageSource.gallery,
imageQuality: 50,
maxWidth: 1600,
maxHeight: 1600);
if (tempFile == null) return;
final MatrixState matrix = Matrix.of(context);
final matrix = Matrix.of(context);
final success = await SimpleDialogs(context).tryRequestWithLoadingDialog(
matrix.client.setAvatar(
MatrixFile(
@ -111,24 +109,20 @@ class _SettingsState extends State<Settings> {
if (wallpaper == null) return;
Matrix.of(context).wallpaper = wallpaper;
await Matrix.of(context)
.client
.storeAPI
.setItem("chat.fluffy.wallpaper", wallpaper.path);
.store
.setItem('chat.fluffy.wallpaper', wallpaper.path);
setState(() => null);
}
void deleteWallpaperAction(BuildContext context) async {
Matrix.of(context).wallpaper = null;
await Matrix.of(context)
.client
.storeAPI
.setItem("chat.fluffy.wallpaper", null);
await Matrix.of(context).store.setItem('chat.fluffy.wallpaper', null);
setState(() => null);
}
@override
Widget build(BuildContext context) {
final Client client = Matrix.of(context).client;
final client = Matrix.of(context).client;
profileFuture ??= client.ownProfile;
profileFuture.then((p) {
if (mounted) setState(() => profile = p);
@ -174,8 +168,9 @@ class _SettingsState extends State<Settings> {
),
),
ThemesSettings(),
if (!kIsWeb && client.storeAPI != null) Divider(thickness: 1),
if (!kIsWeb && client.storeAPI != null)
if (!kIsWeb && Matrix.of(context).store != null)
Divider(thickness: 1),
if (!kIsWeb && Matrix.of(context).store != null)
ListTile(
title: Text(
L10n.of(context).wallpaper,
@ -198,7 +193,7 @@ class _SettingsState extends State<Settings> {
),
onTap: () => deleteWallpaperAction(context),
),
if (!kIsWeb && client.storeAPI != null)
if (!kIsWeb && Matrix.of(context).store != null)
Builder(builder: (context) {
return ListTile(
title: Text(L10n.of(context).changeWallpaper),
@ -223,8 +218,9 @@ class _SettingsState extends State<Settings> {
activeColor: Theme.of(context).primaryColor,
onChanged: (bool newValue) async {
Matrix.of(context).renderHtml = newValue;
await client.storeAPI
.setItem("chat.fluffy.renderHtml", newValue ? "1" : "0");
await Matrix.of(context)
.store
.setItem('chat.fluffy.renderHtml', newValue ? '1' : '0');
setState(() => null);
},
),
@ -300,19 +296,19 @@ class _SettingsState extends State<Settings> {
trailing: Icon(Icons.help),
title: Text(L10n.of(context).help),
onTap: () => launch(
"https://gitlab.com/ChristianPauly/fluffychat-flutter/issues"),
'https://gitlab.com/ChristianPauly/fluffychat-flutter/issues'),
),
ListTile(
trailing: Icon(Icons.link),
title: Text(L10n.of(context).license),
onTap: () => launch(
"https://gitlab.com/ChristianPauly/fluffychat-flutter/raw/master/LICENSE"),
'https://gitlab.com/ChristianPauly/fluffychat-flutter/raw/master/LICENSE'),
),
ListTile(
trailing: Icon(Icons.code),
title: Text(L10n.of(context).sourceCode),
onTap: () => launch(
"https://gitlab.com/ChristianPauly/fluffychat-flutter"),
'https://gitlab.com/ChristianPauly/fluffychat-flutter'),
),
],
),

View file

@ -37,18 +37,18 @@ class DevicesSettingsState extends State<DevicesSettings> {
void _removeDevicesAction(
BuildContext context, List<UserDevice> devices) async {
if (await SimpleDialogs(context).askConfirmation() == false) return;
MatrixState matrix = Matrix.of(context);
List<String> deviceIds = [];
for (UserDevice userDevice in devices) {
var matrix = Matrix.of(context);
var deviceIds = <String>[];
for (var userDevice in devices) {
deviceIds.add(userDevice.deviceId);
}
final success = await SimpleDialogs(context)
.tryRequestWithLoadingDialog(matrix.client.deleteDevices(deviceIds),
onAdditionalAuth: (MatrixException exception) async {
final String password = await SimpleDialogs(context).enterText(
final password = await SimpleDialogs(context).enterText(
titleText: L10n.of(context).pleaseEnterYourPassword,
labelText: L10n.of(context).pleaseEnterYourPassword,
hintText: "******",
hintText: '******',
password: true);
if (password == null) return;
await matrix.client.deleteDevices(deviceIds,
@ -83,9 +83,8 @@ class DevicesSettingsState extends State<DevicesSettings> {
}
Function isOwnDevice = (UserDevice userDevice) =>
userDevice.deviceId == Matrix.of(context).client.deviceID;
final List<UserDevice> devices = List<UserDevice>.from(this.devices);
UserDevice thisDevice =
devices.firstWhere(isOwnDevice, orElse: () => null);
final devices = List<UserDevice>.from(this.devices);
var thisDevice = devices.firstWhere(isOwnDevice, orElse: () => null);
devices.removeWhere(isOwnDevice);
devices.sort((a, b) => b.lastSeenTs.compareTo(a.lastSeenTs));
return Column(
@ -145,13 +144,13 @@ class UserDeviceListItem extends StatelessWidget {
Widget build(BuildContext context) {
return PopupMenuButton(
onSelected: (String action) {
if (action == "remove" && this.remove != null) {
if (action == 'remove' && remove != null) {
remove(userDevice);
}
},
itemBuilder: (BuildContext context) => [
PopupMenuItem<String>(
value: "remove",
value: 'remove',
child: Text(L10n.of(context).removeDevice,
style: TextStyle(color: Colors.red)),
),
@ -175,8 +174,8 @@ class UserDeviceListItem extends StatelessWidget {
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text("${L10n.of(context).id}: ${userDevice.deviceId}"),
Text("${L10n.of(context).lastSeenIp}: ${userDevice.lastSeenIp}"),
Text('${L10n.of(context).id}: ${userDevice.deviceId}'),
Text('${L10n.of(context).lastSeenIp}: ${userDevice.lastSeenIp}'),
],
),
),

View file

@ -1,5 +1,3 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_advanced_networkimage/provider.dart';
@ -55,7 +53,7 @@ class _EmotesSettingsState extends State<EmotesSettings> {
if (readonly) {
return;
}
debugPrint("Saving....");
debugPrint('Saving....');
final client = Matrix.of(context).client;
// be sure to preserve any data not in "short"
Map<String, dynamic> content;
@ -95,7 +93,7 @@ class _EmotesSettingsState extends State<EmotesSettings> {
@override
Widget build(BuildContext context) {
Client client = Matrix.of(context).client;
var client = Matrix.of(context).client;
if (emotes == null) {
emotes = <_EmoteEntry>[];
Map<String, dynamic> emoteSource;
@ -173,7 +171,7 @@ class _EmotesSettingsState extends State<EmotesSettings> {
size: 32.0,
),
onTap: () async {
debugPrint("blah");
debugPrint('blah');
if (newEmoteController.text == null ||
newEmoteController.text.isEmpty ||
newMxcController.text == null ||
@ -374,7 +372,7 @@ class _EmoteImagePickerState extends State<_EmoteImagePicker> {
BotToast.showText(text: L10n.of(context).notSupportedInWeb);
return;
}
File file = await ImagePicker.pickImage(
var file = await ImagePicker.pickImage(
source: ImageSource.gallery,
imageQuality: 50,
maxWidth: 128,

View file

@ -23,7 +23,7 @@ class _SignUpState extends State<SignUp> {
File avatar;
void setAvatarAction() async {
File file = await ImagePicker.pickImage(
var file = await ImagePicker.pickImage(
source: ImageSource.gallery,
maxHeight: 512,
maxWidth: 512,
@ -33,7 +33,7 @@ class _SignUpState extends State<SignUp> {
}
void signUpAction(BuildContext context) async {
MatrixState matrix = Matrix.of(context);
var matrix = Matrix.of(context);
if (usernameController.text.isEmpty) {
setState(() => usernameError = L10n.of(context).pleaseChooseAUsername);
} else {
@ -45,8 +45,8 @@ class _SignUpState extends State<SignUp> {
}
setState(() => loading = true);
final String preferredUsername =
usernameController.text.toLowerCase().replaceAll(" ", "-");
final preferredUsername =
usernameController.text.toLowerCase().replaceAll(' ', '-');
try {
await matrix.client.usernameAvailable(preferredUsername);
@ -83,7 +83,7 @@ class _SignUpState extends State<SignUp> {
children: <Widget>[
Hero(
tag: 'loginBanner',
child: Image.asset("assets/fluffychat-banner.png"),
child: Image.asset('assets/fluffychat-banner.png'),
),
ListTile(
leading: CircleAvatar(

View file

@ -27,7 +27,7 @@ class _SignUpPasswordState extends State<SignUpPassword> {
bool showPassword = true;
void _signUpAction(BuildContext context, {Map<String, dynamic> auth}) async {
MatrixState matrix = Matrix.of(context);
var matrix = Matrix.of(context);
if (passwordController.text.isEmpty) {
setState(() => passwordError = L10n.of(context).pleaseEnterYourPassword);
} else {
@ -40,8 +40,7 @@ class _SignUpPasswordState extends State<SignUpPassword> {
try {
setState(() => loading = true);
Future<LoginState> waitForLogin =
matrix.client.onLoginStateChanged.stream.first;
var waitForLogin = matrix.client.onLoginStateChanged.stream.first;
await matrix.client.register(
username: widget.username,
password: passwordController.text,
@ -51,21 +50,20 @@ class _SignUpPasswordState extends State<SignUpPassword> {
await waitForLogin;
} on MatrixException catch (exception) {
if (exception.requireAdditionalAuthentication) {
final List<String> stages = exception.authenticationFlows
.firstWhere((a) => !a.stages.contains("m.login.email.identity"))
final stages = exception.authenticationFlows
.firstWhere((a) => !a.stages.contains('m.login.email.identity'))
.stages;
final String currentStage =
exception.completedAuthenticationFlows == null
? stages.first
: stages.firstWhere((stage) =>
!exception.completedAuthenticationFlows.contains(stage) ??
true);
final currentStage = exception.completedAuthenticationFlows == null
? stages.first
: stages.firstWhere((stage) =>
!exception.completedAuthenticationFlows.contains(stage) ??
true);
if (currentStage == "m.login.dummy") {
if (currentStage == 'm.login.dummy') {
_signUpAction(context, auth: {
"type": currentStage,
"session": exception.session,
'type': currentStage,
'session': exception.session,
});
} else {
await Navigator.of(context).push(
@ -75,7 +73,7 @@ class _SignUpPasswordState extends State<SignUpPassword> {
currentStage,
exception.session,
() => _signUpAction(context, auth: {
"session": exception.session,
'session': exception.session,
}),
),
),
@ -141,7 +139,7 @@ class _SignUpPasswordState extends State<SignUpPassword> {
autocorrect: false,
onSubmitted: (t) => _signUpAction(context),
decoration: InputDecoration(
hintText: "****",
hintText: '****',
errorText: passwordError,
suffixIcon: IconButton(
icon: Icon(