Modified build.yaml, added logo, made profile screen float
All checks were successful
Build Flutter Web and Docker Image for Local Registry / Build Flutter Web App (push) Successful in 3m7s

This commit is contained in:
whysman 2024-11-09 22:50:56 -05:00
parent 2817b75f2e
commit 996c294153
8 changed files with 141 additions and 80 deletions

View File

@ -44,8 +44,8 @@ jobs:
- name: Build Docker Image - name: Build Docker Image
run: | run: |
docker build -t localhost:5000/pogdark:latest . docker build -t localhost:5000/pogdark-app:latest .
- name: Push Docker Image to Local Registry - name: Push Docker Image to Local Registry
run: | run: |
docker push localhost:5000/pogdark:latest docker push localhost:5000/pogdark-app:latest

View File

Before

Width:  |  Height:  |  Size: 167 KiB

After

Width:  |  Height:  |  Size: 167 KiB

BIN
assets/pogdark_logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

View File

@ -1,4 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'dart:ui';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@ -25,12 +26,36 @@ class MyApp extends StatefulWidget {
class MyAppState extends State<MyApp> { class MyAppState extends State<MyApp> {
Future<void>? _prefsReady; Future<void>? _prefsReady;
bool isProfileActive = false;
bool showProfileInitially = false;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_prefsReady = _prefsReady =
Provider.of<SharedPreferencesProvider>(context, listen: false).ready; Provider.of<SharedPreferencesProvider>(context, listen: false).ready;
_prefsReady!.then((_) {
final prefsProvider =
Provider.of<SharedPreferencesProvider>(context, listen: false);
// Check if username is not set; if so, show ProfileScreen initially
if (prefsProvider.getUserName().isEmpty) {
setState(() {
showProfileInitially = true;
});
}
});
}
void toggleProfileScreen() {
setState(() {
isProfileActive = !isProfileActive;
});
}
void closeInitialProfileScreen() {
setState(() {
showProfileInitially = false;
});
} }
@override @override
@ -53,12 +78,38 @@ class MyAppState extends State<MyApp> {
future: _prefsReady, future: _prefsReady,
builder: (context, snapshot) { builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) { if (snapshot.connectionState == ConnectionState.done) {
if (Provider.of<SharedPreferencesProvider>(context).getUserName() != return Stack(
'') { children: [
return const StatusPage(); StatusPage(toggleProfile: toggleProfileScreen),
} else { if (showProfileInitially)
return const ProfileScreen(isEditing: false); BackdropFilter(
} filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5),
child: Container(
color: Colors.black.withOpacity(0.3),
child: Center(
child: ProfileScreen(
isEditing: false,
onClose:
closeInitialProfileScreen, // Close after setup
),
),
),
),
if (isProfileActive && !showProfileInitially)
BackdropFilter(
filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5),
child: Container(
color: Colors.black.withOpacity(0.3),
child: Center(
child: ProfileScreen(
isEditing: true,
onClose: toggleProfileScreen, // Toggle on edit
),
),
),
),
],
);
} else { } else {
return const Center(child: CircularProgressIndicator()); return const Center(child: CircularProgressIndicator());
} }

View File

@ -8,12 +8,13 @@ import 'package:image_picker/image_picker.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'shared_preferences_provider.dart'; import 'shared_preferences_provider.dart';
import 'status_page.dart';
class ProfileScreen extends StatefulWidget { class ProfileScreen extends StatefulWidget {
final bool isEditing; final bool isEditing;
final VoidCallback onClose;
const ProfileScreen({super.key, required this.isEditing}); const ProfileScreen(
{super.key, required this.isEditing, required this.onClose});
@override @override
ProfileScreenState createState() => ProfileScreenState(); ProfileScreenState createState() => ProfileScreenState();
@ -22,6 +23,7 @@ class ProfileScreen extends StatefulWidget {
class ProfileScreenState extends State<ProfileScreen> { class ProfileScreenState extends State<ProfileScreen> {
final TextEditingController _nameController = TextEditingController(); final TextEditingController _nameController = TextEditingController();
late String? imageData; late String? imageData;
bool isNameEmpty = true;
final ImagePicker _picker = ImagePicker(); final ImagePicker _picker = ImagePicker();
@override @override
@ -31,6 +33,16 @@ class ProfileScreenState extends State<ProfileScreen> {
Provider.of<SharedPreferencesProvider>(context, listen: false); Provider.of<SharedPreferencesProvider>(context, listen: false);
_nameController.text = prefsProvider.getUserName(); _nameController.text = prefsProvider.getUserName();
imageData = prefsProvider.getUserLogo(); imageData = prefsProvider.getUserLogo();
// Check initial state of the name field and add listener
_nameController.addListener(_checkNameField);
isNameEmpty = _nameController.text.trim().isEmpty;
}
void _checkNameField() {
setState(() {
isNameEmpty = _nameController.text.trim().isEmpty;
});
} }
Future<void> _pickImage() async { Future<void> _pickImage() async {
@ -62,13 +74,8 @@ class ProfileScreenState extends State<ProfileScreen> {
await prefsProvider.setUserName(name); await prefsProvider.setUserName(name);
await prefsProvider.setUserLogo(imageData); await prefsProvider.setUserLogo(imageData);
if (widget.isEditing) { // Close the screen after saving if the name is valid
if (mounted) { widget.onClose();
Navigator.pop(context); // Go back to the Status screen if editing
}
} else {
_navigateToStatusScreen(); // Go to Status screen if this is the initial entry
}
} else { } else {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Name cannot be empty!')), const SnackBar(content: Text('Name cannot be empty!')),
@ -77,15 +84,9 @@ class ProfileScreenState extends State<ProfileScreen> {
} }
} }
void _navigateToStatusScreen() {
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => const StatusPage()),
);
}
@override @override
void dispose() { void dispose() {
_nameController.removeListener(_checkNameField);
_nameController.dispose(); _nameController.dispose();
super.dispose(); super.dispose();
} }
@ -103,36 +104,52 @@ class ProfileScreenState extends State<ProfileScreen> {
final prefsProvider = Provider.of<SharedPreferencesProvider>(context); final prefsProvider = Provider.of<SharedPreferencesProvider>(context);
final userLogo = prefsProvider.getUserLogo(); final userLogo = prefsProvider.getUserLogo();
return Scaffold( return Center(
appBar: AppBar( child: Material(
title: Text( color: Colors.white,
widget.isEditing ? 'Edit Profile' : 'Enter Your Name and Logo'), borderRadius: BorderRadius.circular(20.0),
backgroundColor: Colors.blueAccent, elevation: 10.0,
), child: Container(
body: Padding( width: MediaQuery.of(context).size.width * 0.8,
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.min,
children: [ children: [
// Profile Image Display Align(
Center( alignment: Alignment.topRight,
child: CircleAvatar( child: IconButton(
icon: const Icon(Icons.close),
onPressed: isNameEmpty ? null : widget.onClose,
color: isNameEmpty ? Colors.grey : Colors.black,
),
),
Text(
widget.isEditing
? 'Edit Your Information'
: 'Tell Us About Yourself',
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 20),
CircleAvatar(
radius: 50, radius: 50,
backgroundImage: getLogoImage(userLogo).image, backgroundImage: getLogoImage(userLogo).image,
), ),
),
const SizedBox(height: 20), const SizedBox(height: 20),
TextField( TextField(
controller: _nameController, controller: _nameController,
decoration: const InputDecoration( decoration: const InputDecoration(
labelText: 'Name', labelText: 'Your Pet\'s Name',
border: OutlineInputBorder(), border: OutlineInputBorder(),
), ),
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
ElevatedButton( ElevatedButton(
onPressed: _pickImage, onPressed: _pickImage,
child: const Text('Upload Logo'), child: const Text('Upload Profile Image'),
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
ElevatedButton( ElevatedButton(
@ -142,6 +159,7 @@ class ProfileScreenState extends State<ProfileScreen> {
], ],
), ),
), ),
),
); );
} }
} }

View File

@ -16,7 +16,8 @@ class SharedPreferencesProvider extends ChangeNotifier {
Future<void> _initPrefs() async { Future<void> _initPrefs() async {
prefs = await SharedPreferences.getInstance(); prefs = await SharedPreferences.getInstance();
if (prefs.getString('userLogo') == null) { if (prefs.getString('userLogo') == null) {
ByteData bytes = await rootBundle.load('assets/default_logo.png'); ByteData bytes =
await rootBundle.load('assets/default_profile_image.png');
List<int> imageBytes = bytes.buffer.asUint8List(); List<int> imageBytes = bytes.buffer.asUint8List();
prefs.setString('userLogo', base64Encode(imageBytes)); prefs.setString('userLogo', base64Encode(imageBytes));
prefs.setString('id', const Uuid().v4()); prefs.setString('id', const Uuid().v4());

View File

@ -5,11 +5,12 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:web_socket_channel/web_socket_channel.dart'; import 'package:web_socket_channel/web_socket_channel.dart';
import 'profile_screen.dart';
import 'shared_preferences_provider.dart'; import 'shared_preferences_provider.dart';
class StatusPage extends StatefulWidget { class StatusPage extends StatefulWidget {
const StatusPage({super.key}); final VoidCallback toggleProfile;
const StatusPage({super.key, required this.toggleProfile});
@override @override
StatusPageState createState() => StatusPageState(); StatusPageState createState() => StatusPageState();
@ -20,12 +21,10 @@ class StatusPageState extends State<StatusPage> {
Uri.parse('wss://api.pogdark.com:8889/ws'), Uri.parse('wss://api.pogdark.com:8889/ws'),
); );
// Convert the stream to a broadcast stream
late final Stream<dynamic> broadcastStream; late final Stream<dynamic> broadcastStream;
late StreamController<dynamic> controller; late StreamController<dynamic> controller;
List<Map<String, dynamic>> messages = List<Map<String, dynamic>> messages = []; // To hold user messages
[]; // To hold user messages with names and timestamps
@override @override
void initState() { void initState() {
@ -39,14 +38,6 @@ class StatusPageState extends State<StatusPage> {
return messages.where((message) => message['Status'] == status).toList(); return messages.where((message) => message['Status'] == status).toList();
} }
void _navigateToEditProfile() {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const ProfileScreen(isEditing: true)),
);
}
void _sendStatus(String id, String name, String? image, String status) { void _sendStatus(String id, String name, String? image, String status) {
final message = jsonEncode({ final message = jsonEncode({
'Id': id, 'Id': id,
@ -81,7 +72,7 @@ class StatusPageState extends State<StatusPage> {
radius: 20, radius: 20,
backgroundImage: message['Image'] != null backgroundImage: message['Image'] != null
? Image.memory(base64Decode(message['Image'])).image ? Image.memory(base64Decode(message['Image'])).image
: const AssetImage('assets/default_logo.png'), : const AssetImage('assets/default_profile_image.png'),
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
Text( Text(
@ -112,12 +103,15 @@ class StatusPageState extends State<StatusPage> {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: const Text('Pogdark'), title: Image.asset(
'assets/pogdark_logo.png',
height: 40,
),
backgroundColor: Colors.blueAccent, backgroundColor: Colors.blueAccent,
actions: [ actions: [
IconButton( IconButton(
icon: const Icon(Icons.edit), icon: const Icon(Icons.edit),
onPressed: _navigateToEditProfile, onPressed: widget.toggleProfile, // Open ProfileScreen overlay
), ),
], ],
), ),
@ -128,16 +122,14 @@ class StatusPageState extends State<StatusPage> {
final newMessage = final newMessage =
jsonDecode(snapshot.data as String) as Map<String, dynamic>; jsonDecode(snapshot.data as String) as Map<String, dynamic>;
// Update messages only if new or modified
final incomingId = newMessage['Id']; final incomingId = newMessage['Id'];
messages.removeWhere((msg) => msg['Id'] == incomingId); messages.removeWhere((msg) => msg['Id'] == incomingId);
if (newMessage['Status'] != 'expired' || if (newMessage['Status'] != 'expired' &&
newMessage['Status'] != "is leaving") { newMessage['Status'] != "is leaving") {
messages.add(newMessage); messages.add(newMessage);
} }
} }
// Separate messages by status
final onTheWayMessages = _getMessagesByStatus('is on the way'); final onTheWayMessages = _getMessagesByStatus('is on the way');
final arrivedMessages = _getMessagesByStatus('has arrived'); final arrivedMessages = _getMessagesByStatus('has arrived');
@ -148,7 +140,6 @@ class StatusPageState extends State<StatusPage> {
Expanded( Expanded(
child: Row( child: Row(
children: [ children: [
// Column for "On the way" messages
Expanded( Expanded(
child: Column( child: Column(
children: [ children: [
@ -169,7 +160,6 @@ class StatusPageState extends State<StatusPage> {
], ],
), ),
), ),
// Column for "Arrived" messages
Expanded( Expanded(
child: Column( child: Column(
children: [ children: [

View File

@ -70,7 +70,8 @@ flutter:
# 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:
- assets/default_logo.png - assets/default_profile_image.png
- assets/pogdark_logo.png
# - images/a_dot_burr.jpeg # - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg # - images/a_dot_ham.jpeg