start with settings view

This commit is contained in:
otsmr 2025-02-07 22:28:58 +01:00
parent d5bfd5944a
commit 86b6177d42
8 changed files with 394 additions and 102 deletions

View file

@ -7,22 +7,25 @@ plugins {
android { android {
namespace = "com.example.connect" namespace = "com.example.connect"
compileSdk = flutter.compileSdkVersion // compileSdk = flutter.compileSdkVersion
compileSdk 34
//ndkVersion = flutter.ndkVersion //ndkVersion = flutter.ndkVersion
ndkVersion = "25.1.8937393" ndkVersion = "25.1.8937393"
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_17 coreLibraryDesugaringEnabled true
targetCompatibility JavaVersion.VERSION_17 sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
} }
kotlinOptions { kotlinOptions {
jvmTarget = 17 jvmTarget = "1.8"
} }
defaultConfig { defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "eu.twonly" applicationId = "eu.twonly"
multiDexEnabled true
// You can update the following values to match your application needs. // You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config. // For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = flutter.minSdkVersion minSdk = flutter.minSdkVersion
@ -43,3 +46,7 @@ android {
flutter { flutter {
source = "../.." source = "../.."
} }
dependencies {
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.2.2'
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View file

@ -1,4 +1,8 @@
import 'dart:async';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:twonly/src/providers/api/api.dart'; import 'package:twonly/src/providers/api/api.dart';
import 'package:twonly/src/providers/api_provider.dart'; import 'package:twonly/src/providers/api_provider.dart';
@ -15,6 +19,135 @@ import 'src/app.dart';
late DbProvider dbProvider; late DbProvider dbProvider;
late ApiProvider apiProvider; late ApiProvider apiProvider;
/// Streams are created so that app can respond to notification-related events
/// since the plugin is initialized in the `main` function
final StreamController<NotificationResponse> selectNotificationStream =
StreamController<NotificationResponse>.broadcast();
const MethodChannel platform =
MethodChannel('dexterx.dev/flutter_local_notifications_example');
const String portName = 'notification_send_port';
class ReceivedNotification {
ReceivedNotification({
required this.id,
required this.title,
required this.body,
required this.payload,
this.data,
});
final int id;
final String? title;
final String? body;
final String? payload;
final Map<String, dynamic>? data;
}
String? selectedNotificationPayload;
/// A notification action which triggers a url launch event
const String urlLaunchActionId = 'id_1';
/// A notification action which triggers a App navigation event
const String navigationActionId = 'id_3';
/// Defines a iOS/MacOS notification category for text input actions.
const String darwinNotificationCategoryText = 'textCategory';
/// Defines a iOS/MacOS notification category for plain actions.
const String darwinNotificationCategoryPlain = 'plainCategory';
@pragma('vm:entry-point')
void notificationTapBackground(NotificationResponse notificationResponse) {
// ignore: avoid_print
print('notification(${notificationResponse.id}) action tapped: '
'${notificationResponse.actionId} with'
' payload: ${notificationResponse.payload}');
if (notificationResponse.input?.isNotEmpty ?? false) {
// ignore: avoid_print
print(
'notification action tapped with input: ${notificationResponse.input}');
}
}
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
int id = 0;
Future<void> setupPushNotification() async {
const AndroidInitializationSettings initializationSettingsAndroid =
AndroidInitializationSettings("logo");
final List<DarwinNotificationCategory> darwinNotificationCategories =
<DarwinNotificationCategory>[
DarwinNotificationCategory(
darwinNotificationCategoryText,
actions: <DarwinNotificationAction>[
DarwinNotificationAction.text(
'text_1',
'Action 1',
buttonTitle: 'Send',
placeholder: 'Placeholder',
),
],
),
DarwinNotificationCategory(
darwinNotificationCategoryPlain,
actions: <DarwinNotificationAction>[
DarwinNotificationAction.plain('id_1', 'Action 1'),
DarwinNotificationAction.plain(
'id_2',
'Action 2 (destructive)',
options: <DarwinNotificationActionOption>{
DarwinNotificationActionOption.destructive,
},
),
DarwinNotificationAction.plain(
navigationActionId,
'Action 3 (foreground)',
options: <DarwinNotificationActionOption>{
DarwinNotificationActionOption.foreground,
},
),
DarwinNotificationAction.plain(
'id_4',
'Action 4 (auth required)',
options: <DarwinNotificationActionOption>{
DarwinNotificationActionOption.authenticationRequired,
},
),
],
options: <DarwinNotificationCategoryOption>{
DarwinNotificationCategoryOption.hiddenPreviewShowTitle,
},
)
];
/// Note: permissions aren't requested here just to demonstrate that can be
/// done later
final DarwinInitializationSettings initializationSettingsDarwin =
DarwinInitializationSettings(
requestAlertPermission: false,
requestBadgePermission: false,
requestSoundPermission: false,
notificationCategories: darwinNotificationCategories,
);
final InitializationSettings initializationSettings = InitializationSettings(
android: initializationSettingsAndroid,
iOS: initializationSettingsDarwin,
);
await flutterLocalNotificationsPlugin.initialize(
initializationSettings,
onDidReceiveNotificationResponse: selectNotificationStream.add,
onDidReceiveBackgroundNotificationResponse: notificationTapBackground,
);
}
void main() async { void main() async {
final settingsController = SettingsChangeProvider(); final settingsController = SettingsChangeProvider();
@ -34,6 +167,8 @@ void main() async {
} }
}); });
setupPushNotification();
await initMediaStorage(); await initMediaStorage();
dbProvider = DbProvider(); dbProvider = DbProvider();

View file

@ -16,7 +16,7 @@ import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/views/chats/chat_item_details_view.dart'; import 'package:twonly/src/views/chats/chat_item_details_view.dart';
import 'package:twonly/src/views/home_view.dart'; import 'package:twonly/src/views/home_view.dart';
import 'package:twonly/src/views/chats/media_viewer_view.dart'; import 'package:twonly/src/views/chats/media_viewer_view.dart';
import 'package:twonly/src/views/profile_view.dart'; import 'package:twonly/src/views/settings/settings_main_view.dart';
import 'package:twonly/src/views/chats/search_username_view.dart'; import 'package:twonly/src/views/chats/search_username_view.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';

View file

@ -1,87 +0,0 @@
import 'package:provider/provider.dart';
import 'package:twonly/src/model/json/user_data.dart';
import 'package:restart_app/restart_app.dart';
import 'package:flutter/material.dart';
import 'package:twonly/src/providers/settings_change_provider.dart';
import 'package:twonly/src/utils/storage.dart';
class ProfileView extends StatefulWidget {
const ProfileView({super.key});
// final SettingsController settingsController;
@override
State<ProfileView> createState() => _ProfileViewState();
}
class _ProfileViewState extends State<ProfileView> {
final Future<UserData?> _userData = getUser();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: FutureBuilder(
future: _userData,
builder: (context, snap) {
if (snap.hasData) {
return Text("Settings");
// return Text("Hello ${snap.data!.username}!");
} else {
return Container();
}
},
),
),
body: Column(
children: [
Padding(
padding: const EdgeInsets.all(16),
// Glue the SettingsController to the theme selection DropdownButton.
//
// When a user selects a theme from the dropdown list, the
// SettingsController is updated, which rebuilds the MaterialApp.
child: DropdownButton<ThemeMode>(
// Read the selected themeMode from the controller
value: context.watch<SettingsChangeProvider>().themeMode,
// Call the updateThemeMode method any time the user selects a theme.
onChanged: (theme) {
context.read<SettingsChangeProvider>().updateThemeMode(theme);
},
items: const [
DropdownMenuItem(
value: ThemeMode.system,
child: Text('System Theme'),
),
DropdownMenuItem(
value: ThemeMode.light,
child: Text('Light Theme'),
),
DropdownMenuItem(
value: ThemeMode.dark,
child: Text('Dark Theme'),
)
],
),
),
ElevatedButton(
onPressed: () {
showLicensePage(context: context);
},
child: Text('Show Licenses'),
),
FilledButton.icon(
onPressed: () async {
await deleteLocalUserData();
Restart.restartApp(
notificationTitle: 'Successfully logged out',
notificationBody: 'Click here to open the app again',
);
},
label: Text("Logout"),
icon: Icon(Icons.no_accounts),
),
],
));
}
}

View file

@ -0,0 +1,211 @@
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:provider/provider.dart';
import 'package:twonly/main.dart';
import 'package:twonly/src/components/initialsavatar.dart';
import 'package:twonly/src/model/json/user_data.dart';
import 'package:restart_app/restart_app.dart';
import 'package:flutter/material.dart';
import 'package:twonly/src/providers/settings_change_provider.dart';
import 'package:twonly/src/utils/storage.dart';
class ProfileView extends StatefulWidget {
const ProfileView({super.key});
@override
State<ProfileView> createState() => _ProfileViewState();
}
class _ProfileViewState extends State<ProfileView> {
UserData? userData;
@override
void initState() {
super.initState();
initAsync();
}
Future initAsync() async {
userData = await getUser();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Settings"),
),
body: (userData == null)
? null
: ListView(
children: [
Padding(
padding: const EdgeInsets.all(30),
child: Row(
children: [
InitialsAvatar(
displayName: userData!.username,
fontSize: 30,
),
SizedBox(width: 20),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
userData!.displayName,
style: TextStyle(fontSize: 20),
textAlign: TextAlign.left,
),
Text(
userData!.username,
style: TextStyle(
fontSize: 14,
),
textAlign: TextAlign.left,
),
],
),
),
Align(
alignment: Alignment.centerRight,
child: IconButton(
onPressed: () {},
icon: FaIcon(FontAwesomeIcons.qrcode),
),
)
],
),
),
SettingsListTile(
icon: FontAwesomeIcons.user,
text: "Konto",
onTap: () {},
),
SettingsListTile(
icon: FontAwesomeIcons.shieldHeart,
text: "Subscription",
onTap: () {},
),
const Divider(),
SettingsListTile(
icon: FontAwesomeIcons.sun,
text: "Darstellung",
onTap: () {},
),
SettingsListTile(
icon: FontAwesomeIcons.lock,
text: "Datenschutz",
onTap: () {},
),
SettingsListTile(
icon: FontAwesomeIcons.bell,
text: "Benachrichtigungen",
onTap: () async {
const AndroidNotificationDetails
androidNotificationDetails = AndroidNotificationDetails(
'0',
'Messages',
channelDescription: 'Messages from other users.',
importance: Importance.max,
priority: Priority.high,
ticker: 'ticker',
);
const NotificationDetails notificationDetails =
NotificationDetails(
android: androidNotificationDetails);
await flutterLocalNotificationsPlugin.show(0, 'New message',
'You got a new message from XX', notificationDetails,
payload: 'item x');
},
),
const Divider(),
SettingsListTile(
icon: FontAwesomeIcons.circleQuestion,
text: "Help",
onTap: () {},
),
// Padding(
// padding: const EdgeInsets.all(16),
// // Glue the SettingsController to the theme selection DropdownButton.
// //
// // When a user selects a theme from the dropdown list, the
// // SettingsController is updated, which rebuilds the MaterialApp.
// child: DropdownButton<ThemeMode>(
// // Read the selected themeMode from the controller
// value: context.watch<SettingsChangeProvider>().themeMode,
// // Call the updateThemeMode method any time the user selects a theme.
// onChanged: (theme) {
// context
// .read<SettingsChangeProvider>()
// .updateThemeMode(theme);
// },
// items: const [
// DropdownMenuItem(
// value: ThemeMode.system,
// child: Text('System Theme'),
// ),
// DropdownMenuItem(
// value: ThemeMode.light,
// child: Text('Light Theme'),
// ),
// DropdownMenuItem(
// value: ThemeMode.dark,
// child: Text('Dark Theme'),
// )
// ],
// ),
// ),
// ElevatedButton(
// onPressed: () {
// showLicensePage(context: context);
// },
// child: Text('Show Licenses'),
// ),
// FilledButton.icon(
// onPressed: () async {
// await deleteLocalUserData();
// Restart.restartApp(
// notificationTitle: 'Successfully logged out',
// notificationBody: 'Click here to open the app again',
// );
// },
// label: Text("Logout"),
// icon: Icon(Icons.no_accounts),
// ),
],
),
);
}
}
class SettingsListTile extends StatelessWidget {
final IconData icon;
final String text;
final VoidCallback onTap;
const SettingsListTile({
super.key,
required this.icon,
required this.text,
required this.onTap,
});
@override
Widget build(BuildContext context) {
return ListTile(
leading: Padding(
padding: const EdgeInsets.only(
right: 10,
left: 19,
),
child: FaIcon(
icon,
size: 20,
),
),
title: Text(text),
onTap: onTap,
);
}
}

View file

@ -427,6 +427,30 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.0.0" version: "5.0.0"
flutter_local_notifications:
dependency: "direct main"
description:
name: flutter_local_notifications
sha256: ef41ae901e7529e52934feba19ed82827b11baa67336829564aeab3129460610
url: "https://pub.dev"
source: hosted
version: "18.0.1"
flutter_local_notifications_linux:
dependency: transitive
description:
name: flutter_local_notifications_linux
sha256: "8f685642876742c941b29c32030f6f4f6dacd0e4eaecb3efbb187d6a3812ca01"
url: "https://pub.dev"
source: hosted
version: "5.0.0"
flutter_local_notifications_platform_interface:
dependency: transitive
description:
name: flutter_local_notifications_platform_interface
sha256: "6c5b83c86bf819cdb177a9247a3722067dd8cc6313827ce7c77a4b238a26fd52"
url: "https://pub.dev"
source: hosted
version: "8.0.0"
flutter_localizations: flutter_localizations:
dependency: "direct main" dependency: "direct main"
description: flutter description: flutter
@ -1167,6 +1191,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.3" version: "0.7.3"
timezone:
dependency: transitive
description:
name: timezone
sha256: ffc9d5f4d1193534ef051f9254063fa53d588609418c84299956c3db9383587d
url: "https://pub.dev"
source: hosted
version: "0.10.0"
timing: timing:
dependency: transitive dependency: transitive
description: description:

View file

@ -20,6 +20,7 @@ dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
flutter_image_compress: ^2.4.0 flutter_image_compress: ^2.4.0
flutter_local_notifications: ^18.0.1
flutter_localizations: flutter_localizations:
sdk: flutter sdk: flutter
flutter_secure_storage: ^9.2.2 flutter_secure_storage: ^9.2.2
@ -74,13 +75,6 @@ flutter:
assets: assets:
# Add assets from the images directory to the application. # Add assets from the images directory to the application.
- assets/images/ # - assets/images/
- assets/animations/present.lottie.json # - assets/images/logo.jpg
- assets/animations/selfie2.json - assets/
- assets/animations/messages.json
- assets/animations/local.json
- assets/animations/test.json
- assets/animations/product.json
- assets/animations/twonlies.json
- assets/animations/rocket.json
- assets/animations/e2e.json