Add settings manager and move some things around
This commit is contained in:
parent
bc57b20219
commit
9ebe4fc63d
26 changed files with 383 additions and 121 deletions
BIN
assets/images/logo.png
Normal file
BIN
assets/images/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 122 KiB |
BIN
assets/images/logo512.png
Normal file
BIN
assets/images/logo512.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
|
@ -1,7 +1,7 @@
|
||||||
|
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:contacts_plus_plus/api_client.dart';
|
import 'package:contacts_plus_plus/clients/api_client.dart';
|
||||||
import 'package:contacts_plus_plus/models/friend.dart';
|
import 'package:contacts_plus_plus/models/friend.dart';
|
||||||
import 'package:contacts_plus_plus/models/user.dart';
|
import 'package:contacts_plus_plus/models/user.dart';
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:contacts_plus_plus/api_client.dart';
|
import 'package:contacts_plus_plus/clients/api_client.dart';
|
||||||
import 'package:contacts_plus_plus/models/message.dart';
|
import 'package:contacts_plus_plus/models/message.dart';
|
||||||
|
|
||||||
class MessageApi {
|
class MessageApi {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:contacts_plus_plus/api_client.dart';
|
import 'package:contacts_plus_plus/clients/api_client.dart';
|
||||||
import 'package:contacts_plus_plus/models/user.dart';
|
import 'package:contacts_plus_plus/models/user.dart';
|
||||||
|
|
||||||
class UserApi {
|
class UserApi {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:contacts_plus_plus/neos_hub.dart';
|
import 'package:contacts_plus_plus/clients/neos_hub.dart';
|
||||||
|
import 'package:contacts_plus_plus/clients/settings_client.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_phoenix/flutter_phoenix.dart';
|
import 'package:flutter_phoenix/flutter_phoenix.dart';
|
||||||
|
@ -9,7 +10,7 @@ import 'package:http/http.dart' as http;
|
||||||
import 'package:contacts_plus_plus/models/authentication_data.dart';
|
import 'package:contacts_plus_plus/models/authentication_data.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
import 'config.dart';
|
import '../config.dart';
|
||||||
|
|
||||||
class ApiClient {
|
class ApiClient {
|
||||||
static const String userIdKey = "userId";
|
static const String userIdKey = "userId";
|
||||||
|
@ -144,15 +145,19 @@ class ApiClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
class ClientHolder extends InheritedWidget {
|
class ClientHolder extends InheritedWidget {
|
||||||
final ApiClient client;
|
final ApiClient apiClient;
|
||||||
|
final SettingsClient settingsClient;
|
||||||
late final NeosHub hub;
|
late final NeosHub hub;
|
||||||
|
|
||||||
ClientHolder({super.key, required AuthenticationData authenticationData, required super.child})
|
ClientHolder({
|
||||||
: client = ApiClient(authenticationData: authenticationData) {
|
super.key,
|
||||||
hub = NeosHub(apiClient: client);
|
required AuthenticationData authenticationData,
|
||||||
|
required this.settingsClient,
|
||||||
|
required super.child
|
||||||
|
}) : apiClient = ApiClient(authenticationData: authenticationData) {
|
||||||
|
hub = NeosHub(apiClient: apiClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static ClientHolder? maybeOf(BuildContext context) {
|
static ClientHolder? maybeOf(BuildContext context) {
|
||||||
return context.dependOnInheritedWidgetOfExactType<ClientHolder>();
|
return context.dependOnInheritedWidgetOfExactType<ClientHolder>();
|
||||||
}
|
}
|
||||||
|
@ -164,5 +169,8 @@ class ClientHolder extends InheritedWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool updateShouldNotify(covariant ClientHolder oldWidget) => oldWidget.client != client;
|
bool updateShouldNotify(covariant ClientHolder oldWidget) =>
|
||||||
|
oldWidget.apiClient != apiClient
|
||||||
|
|| oldWidget.settingsClient != settingsClient
|
||||||
|
|| oldWidget.hub != hub;
|
||||||
}
|
}
|
|
@ -3,7 +3,7 @@ import 'dart:io';
|
||||||
import 'package:contacts_plus_plus/apis/message_api.dart';
|
import 'package:contacts_plus_plus/apis/message_api.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
|
|
||||||
import 'package:contacts_plus_plus/api_client.dart';
|
import 'package:contacts_plus_plus/clients/api_client.dart';
|
||||||
import 'package:contacts_plus_plus/config.dart';
|
import 'package:contacts_plus_plus/config.dart';
|
||||||
import 'package:contacts_plus_plus/models/message.dart';
|
import 'package:contacts_plus_plus/models/message.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
28
lib/clients/settings_client.dart
Normal file
28
lib/clients/settings_client.dart
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:contacts_plus_plus/models/settings.dart';
|
||||||
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
|
|
||||||
|
|
||||||
|
class SettingsClient {
|
||||||
|
static const String _settingsKey = "settings";
|
||||||
|
static const _storage = FlutterSecureStorage();
|
||||||
|
Settings _currentSettings = Settings.def();
|
||||||
|
|
||||||
|
Settings get currentSettings => _currentSettings;
|
||||||
|
|
||||||
|
Future<void> loadSettings() async {
|
||||||
|
final data = await _storage.read(key: _settingsKey);
|
||||||
|
if (data == null) return;
|
||||||
|
try {
|
||||||
|
_currentSettings = Settings.fromMap(jsonDecode(data));
|
||||||
|
} catch (_) {
|
||||||
|
_storage.delete(key: _settingsKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> changeSettings(Settings newSettings) async {
|
||||||
|
_currentSettings = newSettings;
|
||||||
|
await _storage.write(key: _settingsKey, value: jsonEncode(newSettings.toMap()));
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,20 +1,28 @@
|
||||||
import 'dart:developer';
|
import 'dart:developer';
|
||||||
|
|
||||||
|
import 'package:contacts_plus_plus/clients/settings_client.dart';
|
||||||
import 'package:contacts_plus_plus/widgets/friends_list.dart';
|
import 'package:contacts_plus_plus/widgets/friends_list.dart';
|
||||||
import 'package:contacts_plus_plus/widgets/login_screen.dart';
|
import 'package:contacts_plus_plus/widgets/login_screen.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||||
import 'package:flutter_phoenix/flutter_phoenix.dart';
|
import 'package:flutter_phoenix/flutter_phoenix.dart';
|
||||||
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'api_client.dart';
|
import 'clients/api_client.dart';
|
||||||
import 'models/authentication_data.dart';
|
import 'models/authentication_data.dart';
|
||||||
|
|
||||||
void main() {
|
void main() async {
|
||||||
Logger.root.onRecord.listen((event) => log(event.message, name: event.loggerName));
|
Logger.root.onRecord.listen((event) => log(event.message, name: event.loggerName));
|
||||||
runApp(Phoenix(child: const ContactsPlusPlus()));
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
final settingsClient = SettingsClient();
|
||||||
|
await settingsClient.loadSettings();
|
||||||
|
runApp(Phoenix(child: ContactsPlusPlus(settingsClient: settingsClient,)));
|
||||||
}
|
}
|
||||||
|
|
||||||
class ContactsPlusPlus extends StatefulWidget {
|
class ContactsPlusPlus extends StatefulWidget {
|
||||||
const ContactsPlusPlus({super.key});
|
const ContactsPlusPlus({required this.settingsClient, super.key});
|
||||||
|
|
||||||
|
final SettingsClient settingsClient;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<ContactsPlusPlus> createState() => _ContactsPlusPlusState();
|
State<ContactsPlusPlus> createState() => _ContactsPlusPlusState();
|
||||||
|
@ -27,6 +35,7 @@ class _ContactsPlusPlusState extends State<ContactsPlusPlus> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ClientHolder(
|
return ClientHolder(
|
||||||
|
settingsClient: widget.settingsClient,
|
||||||
authenticationData: _authData,
|
authenticationData: _authData,
|
||||||
child: MaterialApp(
|
child: MaterialApp(
|
||||||
debugShowCheckedModeBanner: false,
|
debugShowCheckedModeBanner: false,
|
||||||
|
@ -40,6 +49,41 @@ class _ContactsPlusPlusState extends State<ContactsPlusPlus> {
|
||||||
const FriendsList() :
|
const FriendsList() :
|
||||||
LoginScreen(
|
LoginScreen(
|
||||||
onLoginSuccessful: (AuthenticationData authData) async {
|
onLoginSuccessful: (AuthenticationData authData) async {
|
||||||
|
final notificationManager = FlutterLocalNotificationsPlugin();
|
||||||
|
final settings = widget.settingsClient.currentSettings;
|
||||||
|
final platformNotifications = notificationManager.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>();
|
||||||
|
if (!settings.notificationsDenied && !(await platformNotifications?.areNotificationsEnabled() ?? true)) {
|
||||||
|
if (context.mounted) {
|
||||||
|
await showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: Text("This app needs to ask your permission to send background notifications."),
|
||||||
|
content: Text("Are you okay with that?"),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () async {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
await widget.settingsClient.changeSettings(settings.copyWith(notificationsDenied: true));
|
||||||
|
},
|
||||||
|
child: const Text("No"),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () async {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
await widget.settingsClient.changeSettings(settings.copyWith(notificationsDenied: false));
|
||||||
|
await notificationManager.resolvePlatformSpecificImplementation<
|
||||||
|
AndroidFlutterLocalNotificationsPlugin>()
|
||||||
|
?.requestPermission();
|
||||||
|
},
|
||||||
|
child: const Text("Yes"),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (authData.isAuthenticated) {
|
if (authData.isAuthenticated) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_authData = authData;
|
_authData = authData;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:developer';
|
import 'dart:developer';
|
||||||
|
|
||||||
import 'package:contacts_plus_plus/api_client.dart';
|
import 'package:contacts_plus_plus/clients/api_client.dart';
|
||||||
import 'package:contacts_plus_plus/apis/message_api.dart';
|
import 'package:contacts_plus_plus/apis/message_api.dart';
|
||||||
import 'package:contacts_plus_plus/auxiliary.dart';
|
import 'package:contacts_plus_plus/auxiliary.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
|
@ -1,5 +1,33 @@
|
||||||
|
|
||||||
class Settings {
|
class Settings {
|
||||||
// No settings right now.
|
static const Settings _defaultSettings = Settings(notificationsDenied: true, unreadCheckIntervalMinutes: 0);
|
||||||
|
final bool notificationsDenied;
|
||||||
|
final int unreadCheckIntervalMinutes;
|
||||||
|
|
||||||
|
const Settings({required this.notificationsDenied, required this.unreadCheckIntervalMinutes});
|
||||||
|
|
||||||
|
factory Settings.def() => _defaultSettings;
|
||||||
|
|
||||||
|
factory Settings.fromMap(Map map) {
|
||||||
|
return Settings(
|
||||||
|
notificationsDenied: map["notificationsDenied"] ?? _defaultSettings.notificationsDenied,
|
||||||
|
unreadCheckIntervalMinutes: map["unreadCheckIntervalMinutes"] ?? _defaultSettings.unreadCheckIntervalMinutes,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map toMap() {
|
||||||
|
return {
|
||||||
|
"notificationsDenied": notificationsDenied,
|
||||||
|
"unreadCheckIntervalMinutes": unreadCheckIntervalMinutes,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Settings copy() => copyWith();
|
||||||
|
|
||||||
|
Settings copyWith({bool? notificationsDenied, int? unreadCheckIntervalMinutes}) {
|
||||||
|
return Settings(
|
||||||
|
notificationsDenied: notificationsDenied ?? this.notificationsDenied,
|
||||||
|
unreadCheckIntervalMinutes: unreadCheckIntervalMinutes ?? this.unreadCheckIntervalMinutes,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
41
lib/widgets/default_error_widget.dart
Normal file
41
lib/widgets/default_error_widget.dart
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class DefaultErrorWidget extends StatelessWidget {
|
||||||
|
const DefaultErrorWidget({required this.message, this.onRetry, super.key});
|
||||||
|
|
||||||
|
final String message;
|
||||||
|
final void Function()? onRetry;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 64, vertical: 128,),
|
||||||
|
child: Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text("Something went wrong: ", style: Theme
|
||||||
|
.of(context)
|
||||||
|
.textTheme
|
||||||
|
.titleMedium,),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Text(message),
|
||||||
|
),
|
||||||
|
if (onRetry != null) TextButton.icon(
|
||||||
|
onPressed: onRetry,
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 16),
|
||||||
|
),
|
||||||
|
icon: const Icon(Icons.refresh),
|
||||||
|
label: const Text("Retry"),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
import 'package:contacts_plus_plus/auxiliary.dart';
|
import 'package:contacts_plus_plus/auxiliary.dart';
|
||||||
import 'package:contacts_plus_plus/models/friend.dart';
|
import 'package:contacts_plus_plus/models/friend.dart';
|
||||||
import 'package:contacts_plus_plus/widgets/generic_avatar.dart';
|
import 'package:contacts_plus_plus/widgets/generic_avatar.dart';
|
||||||
import 'package:contacts_plus_plus/widgets/messages.dart';
|
import 'package:contacts_plus_plus/widgets/messages_list.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class FriendListTile extends StatelessWidget {
|
class FriendListTile extends StatelessWidget {
|
||||||
|
@ -23,7 +23,7 @@ class FriendListTile extends StatelessWidget {
|
||||||
title: Text(friend.username),
|
title: Text(friend.username),
|
||||||
subtitle: Text(friend.userStatus.onlineStatus.name),
|
subtitle: Text(friend.userStatus.onlineStatus.name),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
Navigator.of(context).push(MaterialPageRoute(builder: (context) => Messages(friend: friend)));
|
Navigator.of(context).push(MaterialPageRoute(builder: (context) => MessagesList(friend: friend)));
|
||||||
await onTap?.call();
|
await onTap?.call();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:contacts_plus_plus/api_client.dart';
|
import 'package:contacts_plus_plus/clients/api_client.dart';
|
||||||
import 'package:contacts_plus_plus/apis/friend_api.dart';
|
import 'package:contacts_plus_plus/apis/friend_api.dart';
|
||||||
import 'package:contacts_plus_plus/apis/message_api.dart';
|
import 'package:contacts_plus_plus/apis/message_api.dart';
|
||||||
import 'package:contacts_plus_plus/models/friend.dart';
|
import 'package:contacts_plus_plus/models/friend.dart';
|
||||||
import 'package:contacts_plus_plus/models/message.dart';
|
import 'package:contacts_plus_plus/models/message.dart';
|
||||||
|
import 'package:contacts_plus_plus/widgets/default_error_widget.dart';
|
||||||
import 'package:contacts_plus_plus/widgets/expanding_input_fab.dart';
|
import 'package:contacts_plus_plus/widgets/expanding_input_fab.dart';
|
||||||
import 'package:contacts_plus_plus/widgets/friend_list_tile.dart';
|
import 'package:contacts_plus_plus/widgets/friend_list_tile.dart';
|
||||||
import 'package:contacts_plus_plus/widgets/settings_page.dart';
|
import 'package:contacts_plus_plus/widgets/settings_page.dart';
|
||||||
|
@ -41,12 +42,12 @@ class _FriendsListState extends State<FriendsList> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _refreshFriendsList() {
|
void _refreshFriendsList() {
|
||||||
_friendsFuture = FriendApi.getFriendsList(_clientHolder!.client).then((Iterable<Friend> value) async {
|
_friendsFuture = FriendApi.getFriendsList(_clientHolder!.apiClient).then((Iterable<Friend> value) async {
|
||||||
final unreadMessages = await MessageApi.getUserMessages(_clientHolder!.client, unreadOnly: true);
|
final unreadMessages = await MessageApi.getUserMessages(_clientHolder!.apiClient, unreadOnly: true);
|
||||||
_unreads.clear();
|
_unreads.clear();
|
||||||
|
|
||||||
for (final msg in unreadMessages) {
|
for (final msg in unreadMessages) {
|
||||||
if (msg.senderId != _clientHolder!.client.userId) {
|
if (msg.senderId != _clientHolder!.apiClient.userId) {
|
||||||
final value = _unreads[msg.senderId];
|
final value = _unreads[msg.senderId];
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
_unreads[msg.senderId] = [msg];
|
_unreads[msg.senderId] = [msg];
|
||||||
|
@ -110,7 +111,7 @@ class _FriendsListState extends State<FriendsList> {
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
if (unread.isNotEmpty) {
|
if (unread.isNotEmpty) {
|
||||||
final readBatch = MarkReadBatch(
|
final readBatch = MarkReadBatch(
|
||||||
senderId: _clientHolder!.client.userId,
|
senderId: _clientHolder!.apiClient.userId,
|
||||||
ids: unread.map((e) => e.id).toList(),
|
ids: unread.map((e) => e.id).toList(),
|
||||||
readTime: DateTime.now(),
|
readTime: DateTime.now(),
|
||||||
);
|
);
|
||||||
|
@ -125,18 +126,11 @@ class _FriendsListState extends State<FriendsList> {
|
||||||
);
|
);
|
||||||
} else if (snapshot.hasError) {
|
} else if (snapshot.hasError) {
|
||||||
FlutterError.reportError(FlutterErrorDetails(exception: snapshot.error!, stack: snapshot.stackTrace));
|
FlutterError.reportError(FlutterErrorDetails(exception: snapshot.error!, stack: snapshot.stackTrace));
|
||||||
return Center(
|
return DefaultErrorWidget(
|
||||||
child: Padding(
|
message: "${snapshot.error}",
|
||||||
padding: const EdgeInsets.all(64),
|
onRetry: () {
|
||||||
child: Text(
|
_refreshFriendsList();
|
||||||
"Something went wrong: ${snapshot.error}",
|
},
|
||||||
softWrap: true,
|
|
||||||
style: Theme
|
|
||||||
.of(context)
|
|
||||||
.textTheme
|
|
||||||
.labelMedium,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import 'package:contacts_plus_plus/api_client.dart';
|
import 'package:contacts_plus_plus/clients/api_client.dart';
|
||||||
import 'package:contacts_plus_plus/models/authentication_data.dart';
|
import 'package:contacts_plus_plus/models/authentication_data.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:contacts_plus_plus/api_client.dart';
|
import 'package:contacts_plus_plus/clients/api_client.dart';
|
||||||
import 'package:contacts_plus_plus/auxiliary.dart';
|
import 'package:contacts_plus_plus/auxiliary.dart';
|
||||||
import 'package:contacts_plus_plus/models/message.dart';
|
import 'package:contacts_plus_plus/models/message.dart';
|
||||||
import 'package:contacts_plus_plus/widgets/messages.dart';
|
import 'package:contacts_plus_plus/widgets/messages_list.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:just_audio/just_audio.dart';
|
import 'package:just_audio/just_audio.dart';
|
||||||
|
@ -114,7 +114,7 @@ class _MessageAudioPlayerState extends State<MessageAudioPlayer> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 4,),
|
const SizedBox(width: 4,),
|
||||||
if (widget.message.senderId == ClientHolder.of(context).client.userId) Padding(
|
if (widget.message.senderId == ClientHolder.of(context).apiClient.userId) Padding(
|
||||||
padding: const EdgeInsets.only(right: 12.0),
|
padding: const EdgeInsets.only(right: 12.0),
|
||||||
child: MessageStateIndicator(messageState: widget.message.state),
|
child: MessageStateIndicator(messageState: widget.message.state),
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:contacts_plus_plus/api_client.dart';
|
import 'package:contacts_plus_plus/clients/api_client.dart';
|
||||||
import 'package:contacts_plus_plus/auxiliary.dart';
|
import 'package:contacts_plus_plus/auxiliary.dart';
|
||||||
import 'package:contacts_plus_plus/models/message.dart';
|
import 'package:contacts_plus_plus/models/message.dart';
|
||||||
import 'package:contacts_plus_plus/models/session.dart';
|
import 'package:contacts_plus_plus/models/session.dart';
|
||||||
import 'package:contacts_plus_plus/widgets/generic_avatar.dart';
|
import 'package:contacts_plus_plus/widgets/generic_avatar.dart';
|
||||||
import 'package:contacts_plus_plus/widgets/messages.dart';
|
import 'package:contacts_plus_plus/widgets/messages_list.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ class MessageSessionInvite extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 4,),
|
const SizedBox(width: 4,),
|
||||||
if (message.senderId == ClientHolder.of(context).client.userId) Padding(
|
if (message.senderId == ClientHolder.of(context).apiClient.userId) Padding(
|
||||||
padding: const EdgeInsets.only(right: 12.0),
|
padding: const EdgeInsets.only(right: 12.0),
|
||||||
child: MessageStateIndicator(messageState: message.state),
|
child: MessageStateIndicator(messageState: message.state),
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,27 +1,26 @@
|
||||||
import 'dart:developer';
|
|
||||||
|
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:contacts_plus_plus/api_client.dart';
|
import 'package:contacts_plus_plus/clients/api_client.dart';
|
||||||
import 'package:contacts_plus_plus/auxiliary.dart';
|
import 'package:contacts_plus_plus/auxiliary.dart';
|
||||||
import 'package:contacts_plus_plus/models/friend.dart';
|
import 'package:contacts_plus_plus/models/friend.dart';
|
||||||
import 'package:contacts_plus_plus/models/message.dart';
|
import 'package:contacts_plus_plus/models/message.dart';
|
||||||
import 'package:contacts_plus_plus/models/session.dart';
|
import 'package:contacts_plus_plus/models/session.dart';
|
||||||
|
import 'package:contacts_plus_plus/widgets/default_error_widget.dart';
|
||||||
import 'package:contacts_plus_plus/widgets/message_audio_player.dart';
|
import 'package:contacts_plus_plus/widgets/message_audio_player.dart';
|
||||||
import 'package:contacts_plus_plus/widgets/generic_avatar.dart';
|
import 'package:contacts_plus_plus/widgets/generic_avatar.dart';
|
||||||
import 'package:contacts_plus_plus/widgets/message_session_invite.dart';
|
import 'package:contacts_plus_plus/widgets/message_session_invite.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
class Messages extends StatefulWidget {
|
class MessagesList extends StatefulWidget {
|
||||||
const Messages({required this.friend, super.key});
|
const MessagesList({required this.friend, super.key});
|
||||||
|
|
||||||
final Friend friend;
|
final Friend friend;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<StatefulWidget> createState() => _MessagesState();
|
State<StatefulWidget> createState() => _MessagesListState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _MessagesState extends State<Messages> {
|
class _MessagesListState extends State<MessagesList> {
|
||||||
Future<MessageCache>? _messageCacheFuture;
|
Future<MessageCache>? _messageCacheFuture;
|
||||||
final TextEditingController _messageTextController = TextEditingController();
|
final TextEditingController _messageTextController = TextEditingController();
|
||||||
final ScrollController _sessionListScrollController = ScrollController();
|
final ScrollController _sessionListScrollController = ScrollController();
|
||||||
|
@ -79,8 +78,6 @@ class _MessagesState extends State<Messages> {
|
||||||
_messageScrollController.addListener(() {
|
_messageScrollController.addListener(() {
|
||||||
if (_messageScrollController.position.atEdge && _messageScrollController.position.pixels > 0 &&
|
if (_messageScrollController.position.atEdge && _messageScrollController.position.pixels > 0 &&
|
||||||
_messageScrollController.position.maxScrollExtent > 0 && _messageCacheFutureComplete) {
|
_messageScrollController.position.maxScrollExtent > 0 && _messageCacheFutureComplete) {
|
||||||
log("Top edge hit.");
|
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_messageCacheFutureComplete = false;
|
_messageCacheFutureComplete = false;
|
||||||
_messageCacheFuture = _clientHolder?.hub.getCache(widget.friend.id)
|
_messageCacheFuture = _clientHolder?.hub.getCache(widget.friend.id)
|
||||||
|
@ -94,7 +91,7 @@ class _MessagesState extends State<Messages> {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final apiClient = ClientHolder
|
final apiClient = ClientHolder
|
||||||
.of(context)
|
.of(context)
|
||||||
.client;
|
.apiClient;
|
||||||
var sessions = widget.friend.userStatus.activeSessions;
|
var sessions = widget.friend.userStatus.activeSessions;
|
||||||
final appBarColor = Theme
|
final appBarColor = Theme
|
||||||
.of(context)
|
.of(context)
|
||||||
|
@ -195,38 +192,13 @@ class _MessagesState extends State<Messages> {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
} else if (snapshot.hasError) {
|
} else if (snapshot.hasError) {
|
||||||
return Padding(
|
return DefaultErrorWidget(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 64, vertical: 128,),
|
message: "${snapshot.error}",
|
||||||
child: Center(
|
onRetry: () {
|
||||||
child: Column(
|
setState(() {
|
||||||
mainAxisSize: MainAxisSize.max,
|
_loadMessages();
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
});
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
},
|
||||||
children: [
|
|
||||||
Text("Failed to load messages:", style: Theme
|
|
||||||
.of(context)
|
|
||||||
.textTheme
|
|
||||||
.titleMedium,),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
child: Text("${snapshot.error}"),
|
|
||||||
),
|
|
||||||
TextButton.icon(
|
|
||||||
onPressed: () {
|
|
||||||
setState(() {
|
|
||||||
_loadMessages();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
style: TextButton.styleFrom(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
vertical: 16, horizontal: 16),
|
|
||||||
),
|
|
||||||
icon: const Icon(Icons.refresh),
|
|
||||||
label: const Text("Retry"),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return Column(
|
return Column(
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:contacts_plus_plus/api_client.dart';
|
import 'package:contacts_plus_plus/clients/api_client.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
class SettingsPage extends StatelessWidget {
|
class SettingsPage extends StatelessWidget {
|
||||||
const SettingsPage({super.key});
|
const SettingsPage({super.key});
|
||||||
|
@ -38,7 +39,7 @@ class SettingsPage extends StatelessWidget {
|
||||||
TextButton(onPressed: () => Navigator.of(context).pop(), child: const Text("No")),
|
TextButton(onPressed: () => Navigator.of(context).pop(), child: const Text("No")),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await ClientHolder.of(context).client.logout(context);
|
await ClientHolder.of(context).apiClient.logout(context);
|
||||||
},
|
},
|
||||||
child: const Text("Yes"),
|
child: const Text("Yes"),
|
||||||
),
|
),
|
||||||
|
@ -46,6 +47,35 @@ class SettingsPage extends StatelessWidget {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
shape: Border(
|
||||||
|
bottom: BorderSide(color: Theme.of(context).colorScheme.secondaryContainer, width: 0.5),
|
||||||
|
top: BorderSide(color: Theme.of(context).colorScheme.secondaryContainer, width: 0.5)
|
||||||
|
),
|
||||||
|
trailing: const Icon(Icons.info_outline),
|
||||||
|
title: const Text("About Contacts++"),
|
||||||
|
onTap: () {
|
||||||
|
showAboutDialog(
|
||||||
|
context: context,
|
||||||
|
applicationVersion: "0.0.1",
|
||||||
|
applicationIcon: InkWell(
|
||||||
|
onTap: () async {
|
||||||
|
if (!await launchUrl(Uri.parse("https://github.com/Nutcake/contacts-plus-plus"), mode: LaunchMode.externalApplication)) {
|
||||||
|
if (context.mounted) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("Failed to open link.")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
margin: const EdgeInsets.all(16),
|
||||||
|
constraints: const BoxConstraints(maxWidth: 64),
|
||||||
|
child: Image.asset("assets/images/logo512.png"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
applicationLegalese: "Created by Nutcake with love <3",
|
||||||
|
);
|
||||||
|
},
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -7,9 +7,13 @@
|
||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
|
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
|
||||||
|
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||||
|
|
||||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||||
g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar =
|
g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin");
|
||||||
flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar);
|
flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar);
|
||||||
|
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
|
||||||
|
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
|
||||||
|
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
flutter_secure_storage_linux
|
flutter_secure_storage_linux
|
||||||
|
url_launcher_linux
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
|
136
pubspec.lock
136
pubspec.lock
|
@ -1,6 +1,14 @@
|
||||||
# Generated by pub
|
# Generated by pub
|
||||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||||
packages:
|
packages:
|
||||||
|
args:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: args
|
||||||
|
sha256: "4cab82a83ffef80b262ddedf47a0a8e56ee6fbf7fe21e6e768b02792034dd440"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.0"
|
||||||
async:
|
async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -97,6 +105,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.5"
|
version: "1.0.5"
|
||||||
|
dbus:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: dbus
|
||||||
|
sha256: "6f07cba3f7b3448d42d015bfd3d53fe12e5b36da2423f23838efc1d5fb31a263"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.7.8"
|
||||||
fake_async:
|
fake_async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -150,6 +166,30 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.1"
|
version: "2.0.1"
|
||||||
|
flutter_local_notifications:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_local_notifications
|
||||||
|
sha256: "2876372952b65ca7f684e698eba22bda1cf581fa071dd30ba2f01900f507d0d1"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "14.0.0+1"
|
||||||
|
flutter_local_notifications_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_local_notifications_linux
|
||||||
|
sha256: "909bb95de05a2e793503a2437146285a2f600cd0b3f826e26b870a334d8586d7"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.0.0"
|
||||||
|
flutter_local_notifications_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_local_notifications_platform_interface
|
||||||
|
sha256: "63235c42de5b6c99846969a27ad0209c401e6b77b0498939813725b5791c107c"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "7.0.0"
|
||||||
flutter_phoenix:
|
flutter_phoenix:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -400,6 +440,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.11.1"
|
version: "1.11.1"
|
||||||
|
petitparser:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: petitparser
|
||||||
|
sha256: "49392a45ced973e8d94a85fdb21293fbb40ba805fc49f2965101ae748a3683b4"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.1.0"
|
||||||
platform:
|
platform:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -525,6 +573,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.4.16"
|
version: "0.4.16"
|
||||||
|
timezone:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: timezone
|
||||||
|
sha256: "1cfd8ddc2d1cfd836bc93e67b9be88c3adaeca6f40a00ca999104c30693cdca0"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.9.2"
|
||||||
tuple:
|
tuple:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -541,6 +597,70 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.1"
|
version: "1.3.1"
|
||||||
|
url_launcher:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: url_launcher
|
||||||
|
sha256: "75f2846facd11168d007529d6cd8fcb2b750186bea046af9711f10b907e1587e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.1.10"
|
||||||
|
url_launcher_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_android
|
||||||
|
sha256: "22f8db4a72be26e9e3a4aa3f194b1f7afbc76d20ec141f84be1d787db2155cbd"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.0.31"
|
||||||
|
url_launcher_ios:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_ios
|
||||||
|
sha256: "9af7ea73259886b92199f9e42c116072f05ff9bea2dcb339ab935dfc957392c2"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.1.4"
|
||||||
|
url_launcher_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_linux
|
||||||
|
sha256: "207f4ddda99b95b4d4868320a352d374b0b7e05eefad95a4a26f57da413443f5"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.5"
|
||||||
|
url_launcher_macos:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_macos
|
||||||
|
sha256: "91ee3e75ea9dadf38036200c5d3743518f4a5eb77a8d13fda1ee5764373f185e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.5"
|
||||||
|
url_launcher_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_platform_interface
|
||||||
|
sha256: "6c9ca697a5ae218ce56cece69d46128169a58aa8653c1b01d26fcd4aad8c4370"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.2"
|
||||||
|
url_launcher_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_web
|
||||||
|
sha256: "81fe91b6c4f84f222d186a9d23c73157dc4c8e1c71489c4d08be1ad3b228f1aa"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.16"
|
||||||
|
url_launcher_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_windows
|
||||||
|
sha256: "254708f17f7c20a9c8c471f67d86d76d4a3f9c1591aad1e15292008aceb82771"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.6"
|
||||||
uuid:
|
uuid:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -573,6 +693,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.4"
|
version: "3.1.4"
|
||||||
|
workmanager:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: workmanager
|
||||||
|
sha256: e0be7e35d644643f164ee45d2ce14414f0e0fdde19456aa66065f35a0b1d2ea1
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.5.1"
|
||||||
xdg_directories:
|
xdg_directories:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -581,6 +709,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.0"
|
version: "1.0.0"
|
||||||
|
xml:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: xml
|
||||||
|
sha256: "979ee37d622dec6365e2efa4d906c37470995871fe9ae080d967e192d88286b5"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.2.2"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=2.19.6 <3.0.0"
|
dart: ">=2.19.6 <3.0.0"
|
||||||
flutter: ">=3.3.0"
|
flutter: ">=3.3.0"
|
||||||
|
|
|
@ -47,6 +47,9 @@ dependencies:
|
||||||
html: ^0.15.2
|
html: ^0.15.2
|
||||||
just_audio: ^0.9.32
|
just_audio: ^0.9.32
|
||||||
flutter_phoenix: ^1.1.1
|
flutter_phoenix: ^1.1.1
|
||||||
|
url_launcher: ^6.1.10
|
||||||
|
workmanager: ^0.5.1
|
||||||
|
flutter_local_notifications: ^14.0.0+1
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
@ -71,9 +74,8 @@ flutter:
|
||||||
uses-material-design: true
|
uses-material-design: true
|
||||||
|
|
||||||
# To add assets to your application, add an assets section, like this:
|
# To add assets to your application, add an assets section, like this:
|
||||||
# assets:
|
assets:
|
||||||
# - images/a_dot_burr.jpeg
|
- assets/images/
|
||||||
# - images/a_dot_ham.jpeg
|
|
||||||
|
|
||||||
# An image asset can refer to one or more resolution-specific "variants", see
|
# An image asset can refer to one or more resolution-specific "variants", see
|
||||||
# https://flutter.dev/assets-and-images/#resolution-aware
|
# https://flutter.dev/assets-and-images/#resolution-aware
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
// This is a basic Flutter widget test.
|
|
||||||
//
|
|
||||||
// To perform an interaction with a widget in your test, use the WidgetTester
|
|
||||||
// utility in the flutter_test package. For example, you can send tap and scroll
|
|
||||||
// gestures. You can also use WidgetTester to find child widgets in the widget
|
|
||||||
// tree, read text, and verify that the values of widget properties are correct.
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
|
||||||
|
|
||||||
import 'package:contacts_plus_plus/main.dart';
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
|
|
||||||
// Build our app and trigger a frame.
|
|
||||||
await tester.pumpWidget(const ContactsPlusPlus());
|
|
||||||
|
|
||||||
// Verify that our counter starts at 0.
|
|
||||||
expect(find.text('0'), findsOneWidget);
|
|
||||||
expect(find.text('1'), findsNothing);
|
|
||||||
|
|
||||||
// Tap the '+' icon and trigger a frame.
|
|
||||||
await tester.tap(find.byIcon(Icons.add));
|
|
||||||
await tester.pump();
|
|
||||||
|
|
||||||
// Verify that our counter has incremented.
|
|
||||||
expect(find.text('0'), findsNothing);
|
|
||||||
expect(find.text('1'), findsOneWidget);
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -7,8 +7,11 @@
|
||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
|
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
|
||||||
|
#include <url_launcher_windows/url_launcher_windows.h>
|
||||||
|
|
||||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
|
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
|
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
|
||||||
|
UrlLauncherWindowsRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
flutter_secure_storage_windows
|
flutter_secure_storage_windows
|
||||||
|
url_launcher_windows
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
|
Loading…
Reference in a new issue