design + logic updates

url-works
Christopher Schmitt 2024-01-01 18:31:10 +01:00
parent 9b5b09045b
commit 79380eb2e0
19 changed files with 1125 additions and 301 deletions

View File

@ -1,11 +1,13 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:moody/utils/SlideDirection.dart';
import 'package:moody/views/color_page/color_page.dart';
import 'package:moody/views/entry_view/entry_page.dart';
import 'package:moody/views/first_page/first_page.dart';
import 'package:moody/views/home_page/home_page.dart';
import 'package:moody/views/home_page/kalendartryhome_page.dart';
import 'package:moody/views/settings_page/settings_page.dart';
import 'package:moody/views/start_page/start_page.dart';
import 'package:moody/views/statistic/statistic_page.dart';
import 'package:moody/views/write_page/write_page.dart';
@ -16,7 +18,7 @@ final GoRouter _router = GoRouter(
GoRoute(
path: '/',
pageBuilder: (context, state) =>
_noAnimationTransition(context, state, FirstPage()),
_noAnimationTransition(context, state, StartPage()),
),
GoRoute(
path: '/moods',
@ -34,9 +36,19 @@ final GoRouter _router = GoRouter(
),
GoRoute(
path: '/home',
pageBuilder: (context, state) =>
_noAnimationTransition(context, state, HomePage()),
),
pageBuilder: (context, state) =>
_noAnimationTransition(context, state, HomePage()),
),
GoRoute(
path: '/first',
pageBuilder: (context, state) =>
_noAnimationTransition(context, state, FirstPage()),
),
GoRoute(
path: '/color',
pageBuilder: (context, state) =>
_noAnimationTransition(context, state, ColorPage()),
),
GoRoute(
path: '/write',
builder: (context, state) {

View File

@ -1,21 +1,21 @@
import 'dart:math' as Math;
import 'package:flutter/material.dart';
class CirclePainter extends CustomPainter {
final double value;
final Color color; // New field for color
CirclePainter(this.value);
CirclePainter(this.value, this.color);
@override
void paint(Canvas canvas, Size size) {
var paint = Paint()
..color = Colors.greenAccent.withOpacity(0.5)
..color = color.withOpacity(0.5) // Use the passed color
..style = PaintingStyle.fill;
double radius = value * 500 / 100;
print(radius);
double radius = Math.pow(((value + 5) * 500 / 100) / 1.5, 1.14).toDouble();
canvas.drawCircle(Offset(size.width / 6, size.height / 4), radius, paint);
//print(size.width / 6);
}
@override

View File

@ -0,0 +1,44 @@
import 'package:flutter/material.dart';
class ColorPair {
final Color textColor;
final Color backgroundColor;
final String name;
ColorPair(
{required this.name,
required this.textColor,
required this.backgroundColor});
}
List<ColorPair> colorPairs = [
ColorPair(
name: "origin",
textColor: Color(0xff446F2F),
backgroundColor: Color(0xffC9E7BB)),
ColorPair(
name: "lavender",
textColor: Color(0xff765EAB),
backgroundColor: Color(0xffD3CAE7)),
ColorPair(
name: "orange",
textColor: Color(0xffC78233),
backgroundColor: Color(0xffF0CFA9)),
ColorPair(
name: "rose",
textColor: Color(0xffB15555),
backgroundColor: Color(0xffEBD6D6)),
ColorPair(
name: "mountain",
textColor: Color(0xff1D6289),
backgroundColor: Color(0xffB4D0E0)),
ColorPair(
name: "moon",
textColor: Color(0xff7A83C7),
backgroundColor: Color(0xff393C55)),
ColorPair(
name: "forest",
textColor: Color(0xffC9E7BB),
backgroundColor: Color(0xff4C5847)),
// ...add other color pairs with names...
];

View File

@ -3,28 +3,37 @@ import 'dart:ui';
import 'package:shared_preferences/shared_preferences.dart';
import '../definitions/ColorPairs.dart';
class PreferencesService {
static const _keyColor1 = 'color1';
static const _keyColor2 = 'color2';
static const _keyDiaryEntries = 'diaryEntries';
static const _keyPin = 'userPin';
static const _keyPinEnabled = 'pinEnabled';
// Load color preferences
Future<ColorPreference> loadColors() async {
// Helper method to save a ColorPair preference
Future<void> saveColorPair(ColorPair colorPair) async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString(
_keyColor1, colorPair.backgroundColor.value.toString());
await prefs.setString(_keyColor2, colorPair.textColor.value.toString());
}
// Helper method to load the ColorPair preference
Future<ColorPair> loadColorPair() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
final String? color1 = prefs.getString(_keyColor1);
final String? color2 = prefs.getString(_keyColor2);
return ColorPreference(
color1: color1 != null ? Color(int.parse(color1)) : null,
color2: color2 != null ? Color(int.parse(color2)) : null,
);
}
// Save color preferences
Future<void> saveColors(Color color1, Color color2) async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString(_keyColor1, color1.value.toString());
await prefs.setString(_keyColor2, color2.value.toString());
if (color1 != null && color2 != null) {
return ColorPair(
name: "Custom", // You might want to handle naming differently
textColor: Color(int.parse(color2)),
backgroundColor: Color(int.parse(color1)),
);
}
return colorPairs[0]; // Return null if no color pair is set
}
// Get the diary entry for the current date
@ -113,6 +122,42 @@ class PreferencesService {
);
}).toList();
}
// Set PIN method
Future<void> setPin(int pin) async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setInt(_keyPin, pin);
await prefs.setBool(
_keyPinEnabled, true); // Automatically enable PIN on setting
}
// Check PIN method
Future<bool> checkPin(int pin) async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
int savedPin = prefs.getInt(_keyPin) ?? -1; // Default to -1 if not set
return savedPin == pin;
}
// Check if PIN is enabled
Future<bool> isPinEnabled() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
return prefs.getBool(_keyPinEnabled) ?? false; // Return false if not set
}
// Enable PIN
Future<void> enablePin() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
if (prefs.getInt(_keyPin) != null) {
// Ensure PIN exists before enabling
await prefs.setBool(_keyPinEnabled, true);
}
}
// Disable PIN
Future<void> disablePin() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setBool(_keyPinEnabled, false);
}
}
// Helper class to store color preferences

View File

@ -5,11 +5,11 @@ class QuestionGenerator {
"Welche Begebenheit hat heute dein Herz am meisten erfreut?",
"Was war das Highlight deines Tages, das dir ein Lächeln geschenkt hat?",
"Welche besondere Erfahrung heute hat dir neue Energie und Freude gebracht?",
"gut?",
"Wie gehts?",
"What's up?",
"How arr you",
"You ok?",
"how fullfilled do you feel today?",
"how fullfilled do you feel today?",
"how fullfilled do you feel today?",
"how fullfilled do you feel today?",
"how fullfilled do you feel today?",
];
String getQuestionByDate(DateTime date) {

View File

@ -1,5 +1,7 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:moody/utils/definitions/ColorPairs.dart';
import 'package:moody/utils/logic/PreferencesService.dart';
class CustomBottomNavigationBar extends StatefulWidget {
final int initialSelectedIndex;
@ -47,6 +49,28 @@ class _CustomBottomNavigationBarState extends State<CustomBottomNavigationBar> {
@override
Widget build(BuildContext context) {
return FutureBuilder<ColorPair>(
future:
PreferencesService().loadColorPair(), // Async loading of the color
builder: (BuildContext context, AsyncSnapshot<ColorPair> snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData) {
// When data is loaded
Color backgroundColor =
snapshot.data!.backgroundColor; // Use loaded background color
return buildNavigationBar(backgroundColor);
} else {
// While loading or if no data, show default or loading indicator
return buildNavigationBar(
Colors.white); // Default color or loading indicator
}
},
);
}
@override
Widget buildNavigationBar(Color backgroundColor) {
Future<ColorPair> globalColors = PreferencesService().loadColorPair();
return Container(
width: 175,
margin: EdgeInsets.only(left: 130, right: 130, bottom: 30),
@ -77,22 +101,25 @@ class _CustomBottomNavigationBarState extends State<CustomBottomNavigationBar> {
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
_buildNavItem(Image.asset('assets/icons/icon-analyze.png'), 0),
_buildNavItem(Image.asset('assets/icons/icon-logo.png'), 1),
_buildNavItem(Image.asset('assets/icons/icon-settings.png'), 2),
_buildNavItem(Image.asset('assets/icons/icon-analyze.png'), 0,
backgroundColor),
_buildNavItem(
Image.asset('assets/icons/icon-logo.png'), 1, backgroundColor),
_buildNavItem(Image.asset('assets/icons/icon-settings.png'), 2,
backgroundColor),
],
),
),
);
}
Widget _buildNavItem(Image icon, int index) {
Widget _buildNavItem(Image icon, int index, Color selectedColor) {
return Container(
height: 40,
width:40,
width: 40,
decoration: _selectedIndex == index
? BoxDecoration(
color: Colors.lightGreen,
color: selectedColor,
borderRadius: BorderRadius.circular(40),
)
: null,

View File

@ -2,14 +2,13 @@ import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:moody/utils/logic/QuestionGenerator.dart';
import '../CirclePainter.dart';
class QuestionSliderWidget extends StatefulWidget {
final DateTime date;
final String questionText;
final double initialSliderValue;
final bool isSliderEnabled;
final ValueChanged<double> onSliderChanged;
final ValueChanged<SliderChangeData> onSliderChanged;
final ValueChanged<double> onSliderPositionChanged;
QuestionSliderWidget({
Key? key,
@ -18,6 +17,7 @@ class QuestionSliderWidget extends StatefulWidget {
required this.initialSliderValue,
required this.isSliderEnabled,
required this.onSliderChanged,
required this.onSliderPositionChanged,
}) : super(key: key);
@override
@ -25,34 +25,30 @@ class QuestionSliderWidget extends StatefulWidget {
}
class _QuestionSliderWidgetState extends State<QuestionSliderWidget> {
late double _sliderValue;
bool _sliderChanged = false;
bool _showDragText = true;
SliderChangeData _sliderData = SliderChangeData(0, 0);
QuestionGenerator generator = QuestionGenerator();
@override
void initState() {
super.initState();
_sliderValue = widget.initialSliderValue;
_sliderData.value = widget.initialSliderValue;
}
@override
Widget build(BuildContext context) {
return Expanded(
child: Stack(
children: [
Positioned.fill(
top: 20,
left: _calculateSliderPosition(context, _sliderValue) - 60,
child: CustomPaint(
painter: CirclePainter(_sliderValue),
),
),
return Padding(
padding: const EdgeInsets.fromLTRB(0, 150, 20, 0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
// Date
Padding(
padding: const EdgeInsets.fromLTRB(20, 150, 20, 20),
padding: const EdgeInsets.fromLTRB(30, 0, 0, 30),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
// Date
children: [
Text(
DateFormat('dd MM yyyy').format(widget.date),
style: TextStyle(fontSize: 18),
@ -62,10 +58,17 @@ class _QuestionSliderWidgetState extends State<QuestionSliderWidget> {
generator.getQuestionByDate(widget.date),
style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold),
),
// Slider and label
SliderTheme(
],
),
),
// Slider and label
Stack(
children: [
Padding(
padding: const EdgeInsets.fromLTRB(15, 0, 0, 15),
child: SliderTheme(
data: SliderTheme.of(context).copyWith(
disabledThumbColor: Colors.amber,
disabledThumbColor: Colors.transparent,
activeTrackColor: Colors.black,
trackShape: RectangularSliderTrackShape(),
inactiveTickMarkColor: Colors.red,
@ -77,7 +80,7 @@ class _QuestionSliderWidgetState extends State<QuestionSliderWidget> {
trackHeight: 2,
thumbColor: Colors.transparent,
thumbShape: RoundSliderThumbShape(
enabledThumbRadius: 30.0,
enabledThumbRadius: 10.0,
elevation: 0,
disabledThumbRadius: 0),
rangeThumbShape: RoundRangeSliderThumbShape(
@ -86,35 +89,40 @@ class _QuestionSliderWidgetState extends State<QuestionSliderWidget> {
elevation: 0,
),
),
child: Stack(
alignment: Alignment.centerLeft,
children: [
Slider(
min: 0.0,
max: 100.0,
value: _sliderValue,
onChanged: widget.isSliderEnabled
? (value) {
setState(() {
_sliderValue = value;
_sliderChanged = true;
});
widget.onSliderChanged(value);
}
: null,
),
Positioned(
left: _calculateSliderPosition(context, _sliderValue),
child: Text(
"${_sliderValue.toStringAsFixed(0)}%",
style: TextStyle(fontSize: 20), // Size increased by 2
),
),
],
child: Slider(
min: 0.0,
max: 100.0,
value: _sliderData.value,
onChanged: widget.isSliderEnabled
? (value) {
setState(() {
_sliderData.position =
_calculateSliderPosition(context, value);
_sliderData.value = value;
_showDragText = false;
});
widget.onSliderChanged(SliderChangeData(
_sliderData.value, _sliderData.position));
}
: null,
),
),
],
),
),
Positioned(
left: _calculateSliderPosition(context, _sliderData.value),
child: IgnorePointer(
child: _showDragText
? Text(
"Drag Me -->",
style: TextStyle(fontSize: 16),
)
: Text(
"${_sliderData.value.toStringAsFixed(0)}%",
style: TextStyle(fontSize: 20),
),
),
),
],
),
],
),
@ -128,3 +136,10 @@ class _QuestionSliderWidgetState extends State<QuestionSliderWidget> {
return (sliderWidth - thumbWidth) * value / 100.0 + thumbWidth / 2;
}
}
class SliderChangeData {
double value;
double position; // Additional data you might want to include
SliderChangeData(this.value, this.position);
}

View File

@ -0,0 +1,120 @@
import 'package:flutter/material.dart';
import '../../utils/definitions/ColorPairs.dart';
import '../../utils/logic/PreferencesService.dart';
import '../../utils/widgets/BottomNavigationWidget.dart';
// ...Include PreferencesService and other necessary imports
class ColorPage extends StatefulWidget {
@override
_ColorPageState createState() => _ColorPageState();
}
class _ColorPageState extends State<ColorPage> {
int? selectedColorIndex; // Index of the selected color
@override
void initState() {
super.initState();
_loadSelectedColor();
}
// Load selected color from preferences
void _loadSelectedColor() async {
ColorPair? savedColorPair = await PreferencesService().loadColorPair();
if (savedColorPair != null) {
// Find the index of the saved color in the colorPairs list
int index = colorPairs.indexWhere(
(pair) =>
pair.backgroundColor.value ==
savedColorPair.backgroundColor.value &&
pair.textColor.value == savedColorPair.textColor.value,
);
if (index != -1) {
setState(() {
selectedColorIndex = index;
});
}
} else {
// If no color is selected, default to the first one
setState(() {
selectedColorIndex = 0;
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.fromLTRB(30, 150, 30, 30),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(bottom: 50),
child: Row(mainAxisSize: MainAxisSize.min, children: [
Icon(Icons.chevron_left, color: Colors.black), // "<" icon
Text("settings",
style: TextStyle(color: Colors.black, fontSize: 18)),
]),
),
Text("change color", style: TextStyle(fontSize: 24)),
SizedBox(height: 30),
Wrap(
spacing: 15, // space between circles horizontally
runSpacing: 15, // space between circles vertically
children: List.generate(
colorPairs.length, (index) => buildColorCircle(index)),
),
Padding(
padding: const EdgeInsets.only(top: 50.0),
child: Text("Can't find your favorite color?",
style: TextStyle(color: Colors.grey)),
),
Text("Wish you a color!", style: TextStyle(color: Colors.grey)),
],
),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
floatingActionButton: CustomBottomNavigationBar(initialSelectedIndex: 2),
);
}
// Helper function to build a single color circle
Widget buildColorCircle(int index) {
return GestureDetector(
onTap: () async {
setState(() {
selectedColorIndex = index; // Set selected color
});
await PreferencesService().saveColorPair(colorPairs[index]);
},
child: Container(
width: 120,
height: 120,
padding: EdgeInsets.all(10), // Padding for inner circle effect
decoration: BoxDecoration(
color: colorPairs[index].backgroundColor,
shape: BoxShape.circle,
),
child: Container(
decoration: BoxDecoration(
border: selectedColorIndex == index
? Border.all(color: Colors.black, width: 1)
: null,
shape: BoxShape.circle,
),
child: Center(
child: Text(
colorPairs[index].name,
style:
TextStyle(color: colorPairs[index].textColor, fontSize: 16),
),
),
),
),
);
}
}

View File

@ -2,6 +2,8 @@ import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:moody/utils/widgets/QuestionSliderWidget.dart';
import '../../utils/CirclePainter.dart';
import '../../utils/definitions/ColorPairs.dart';
import '../../utils/logic/PreferencesService.dart';
class FirstPage extends StatefulWidget {
@ -10,10 +12,28 @@ class FirstPage extends StatefulWidget {
}
class _FirstPageState extends State<FirstPage> {
SliderChangeData sliderData = SliderChangeData(0, 0);
double _sliderValue = 0.0;
bool _sliderChanged = false;
PreferencesService _prefsService = PreferencesService();
Color backgroundColor = Colors.lightGreenAccent;
Color textColor = Colors.black;
@override
void initState() {
super.initState();
_loadColor();
}
void _loadColor() async {
ColorPair colorPair = await PreferencesService().loadColorPair();
setState(() {
backgroundColor = colorPair.backgroundColor;
textColor = colorPair.textColor;
});
}
void _saveEntry() async {
try {
List<String> texts = [];
@ -32,64 +52,77 @@ class _FirstPageState extends State<FirstPage> {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: SafeArea(
child: Stack(
children: [
// Background circle
/*Positioned.fill(
child: CustomPaint(
painter: CirclePainter(_sliderValue),
),
),*/
// Main content
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
// Date
QuestionSliderWidget(
date: DateTime.now(),
questionText: "How are you today",
initialSliderValue: 0,
isSliderEnabled: true,
onSliderChanged: (value) {
setState(() {
_sliderValue = value;
});
}),
// Why? button
TextButton(
onPressed: () {
context.go('/write', extra: _sliderValue);
// Implement your why? functionality here
},
child: Text("Why?"),
),
],
),
// Skip/Save button
Positioned(
bottom: 20,
right: 20,
child: TextButton(
onPressed: () {
print(_sliderChanged);
if (_sliderChanged) {
_saveEntry();
context.go('/home', extra: _sliderValue);
} else {
context.go('/home', extra: 0);
}
},
child: Text(_sliderChanged ? "Save" : "Skip"),
return backgroundColor == Colors.lightGreenAccent
? CircularProgressIndicator() // Show loading indicator while color is null
: MaterialApp(
home: Scaffold(
body: SafeArea(
child: Stack(
children: [
// Background circle
Positioned.fill(
top: 15,
left: sliderData.position - 70,
child: CustomPaint(
painter:
CirclePainter(sliderData.value, backgroundColor),
),
),
// Main content
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
// Date
Stack(
children: [
QuestionSliderWidget(
onSliderPositionChanged: (value) {
print("sliderposchanged");
},
date: DateTime.now(),
questionText: "",
initialSliderValue: 0,
isSliderEnabled: true,
onSliderChanged: (value) {
setState(() {
sliderData = value;
if (!_sliderChanged) _sliderChanged = true;
});
}),
],
),
// Why? button
_sliderChanged
? TextButton(
onPressed: () {
context.go('/write', extra: sliderData.value);
},
child: Text("warum?"),
)
: SizedBox.shrink(),
],
),
// Skip/Save button
Positioned(
bottom: 20,
right: 20,
child: TextButton(
onPressed: () {
print(_sliderChanged);
if (_sliderChanged) {
_saveEntry();
context.go('/home', extra: sliderData.value);
} else {
context.go('/home', extra: 0);
}
},
child: Text(_sliderChanged ? "Save" : "Skip"),
),
),
],
),
),
],
),
),
),
);
),
);
}
}

View File

@ -18,7 +18,7 @@ class _DragWidgetState extends State<DragWidget> {
children: [
Positioned.fill(
child: CustomPaint(
painter: CirclePainter(_sliderValue),
painter: CirclePainter(_sliderValue, Colors.red),
),
),
Column(

View File

@ -68,20 +68,23 @@ class _HomePageState extends State<HomePage> {
// Background circle
Positioned.fill(
child: CustomPaint(
painter: CirclePainter(_sliderValue),
painter: CirclePainter(_sliderValue, Colors.orange),
),
),
SingleChildScrollView(
child: Column(
children: [
QuestionSliderWidget(
onSliderPositionChanged: (value) {
print("sliderposchanged");
},
date: DateTime.now(),
questionText: 'this is how it is:',
initialSliderValue: _sliderValue,
isSliderEnabled: true,
onSliderChanged: (value) {
setState(() {
_sliderValue = value;
_sliderValue = value.value;
});
},
),

View File

@ -1,102 +1,124 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import '../../utils/widgets/BottomNavigationWidget.dart';
import 'widgets/CustomDivider.dart';
import 'widgets/SetPinPopup.dart';
import 'widgets/TextSwitchContainer.dart';
class SettingsPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
children: <Widget>[
Container(
padding: EdgeInsets.symmetric(vertical: 16.0),
backgroundColor: Color(0xffeeeeee),
body: Container(
padding: EdgeInsets.only(bottom: 0),
margin: const EdgeInsets.fromLTRB(30, 30, 30, 0),
child: SingleChildScrollView(
child: Column(
children: <Widget>[
const SizedBox(
height: 100,
),
// Settings section
Container(
margin: EdgeInsets.only(bottom: 10),
alignment: Alignment.topLeft,
child: Text('settings',
style:
TextStyle(fontWeight: FontWeight.w500, fontSize: 24)),
),
TextSwitchContainer(
leftText: "change color",
onTap: () => context.go("/color"),
),
CustomDivider(),
TextSwitchContainer(
leftText: "pin",
hasSwitch: true,
onTap: () => {showSetPinPopup(context)},
onSwitchToggle: (value) => print('Switch toggled: $value'),
),
CustomDivider(),
TextSwitchContainer(
leftText: "your data",
onTap: () => print('Container tapped'),
),
// Community section
Padding(
padding: const EdgeInsets.fromLTRB(0, 50, 0, 10),
child: Container(
alignment: Alignment.topLeft,
margin: EdgeInsets.only(bottom: 10),
child: Text('community',
style:
TextStyle(fontWeight: FontWeight.w500, fontSize: 24)),
),
),
TextSwitchContainer(
leftText: "join our discord",
rightText: "fullfilled",
onTap: () => print('Container tapped'),
),
CustomDivider(),
TextSwitchContainer(
leftText: "our instagram",
rightText: "@get.fullfilled",
onTap: () => print('Container tapped'),
),
CustomDivider(),
TextSwitchContainer(
leftText: "share fullfilled with your loved ones",
onTap: () => print('Container tapped'),
),
Padding(
padding: const EdgeInsets.fromLTRB(0, 50, 0, 10),
child: Container(
alignment: Alignment.topLeft,
child: Text('humans behind fullfilled',
style:
TextStyle(fontWeight: FontWeight.w500, fontSize: 24)),
),
),
TextSwitchContainer(
leftText: "about us",
onTap: () => print('Container tapped'),
),
CustomDivider(),
TextSwitchContainer(
leftText: "support us",
onTap: () => print('Container tapped'),
),
CustomDivider(),
TextSwitchContainer(
leftText: "give feedback",
onTap: () => print('Container tapped'),
),
//
ListTile(
title: Text('imprint & privacy policy',
style: TextStyle(color: Colors.grey, fontSize: 15)),
onTap: () => {}, // Implement your logic
),
SizedBox(
height: 150,
)
],
),
// Settings section
ListTile(
title:
Text('settings', style: TextStyle(fontWeight: FontWeight.bold)),
),
ListTile(
title: Text('change color'),
onTap: () => {}, // Implement your logic
),
Divider(),
// Community section
ListTile(
title: Text('notifications'),
onTap: () => {}, // Implement your logic
),
Divider(),
// Community section
ListTile(
title: Text('set pin'),
onTap: () => {}, // Implement your logic
),
Divider(),
// Community section
ListTile(
title: Text('export your data'),
onTap: () => {}, // Implement your logic
),
Container(
padding: EdgeInsets.symmetric(vertical: 16.0),
),
ListTile(
title: Text('community',
style: TextStyle(fontWeight: FontWeight.bold)),
),
ListTile(
title: Text('join our discord'),
trailing: Text('fulfilled'),
onTap: () => {}, // Implement your logic
),
Divider(),
//
ListTile(
title: Text('our instagram'),
trailing: Text('@get_fulfilled'),
onTap: () => {}, // Implement your logic
),
Divider(),
//
ListTile(
title: Text('share fulfilled with your loved ones'),
onTap: () => {}, // Implement your logic
),
Container(
padding: EdgeInsets.symmetric(vertical: 16.0),
),
ListTile(
title: Text('humans behind fulfilled',
style: TextStyle(fontWeight: FontWeight.bold)),
),
ListTile(
title: Text('about us'),
onTap: () => {}, // Implement your logic
),
Divider(),
//
ListTile(
title: Text('support us'),
onTap: () => {}, // Implement your logic
),
Divider(),
//
ListTile(
title: Text('give feedback'),
onTap: () => {}, // Implement your logic
),
Divider(),
//
ListTile(
title: Text('imprint & privacy policy',
style: TextStyle(color: Colors.grey)),
onTap: () => {}, // Implement your logic
),
],
),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
floatingActionButton: CustomBottomNavigationBar(initialSelectedIndex: 3),
floatingActionButton: CustomBottomNavigationBar(initialSelectedIndex: 2),
);
}
void showSetPinPopup(BuildContext context) {
showDialog(
context: context,
builder: (BuildContext context) {
return SetPinPopup();
},
);
}
}

View File

@ -0,0 +1,14 @@
import 'package:flutter/material.dart';
class CustomDivider extends StatelessWidget {
const CustomDivider({super.key});
@override
Widget build(BuildContext context) {
return Divider(
color: Colors.grey.shade300,
thickness: 1, // Set thickness to 1px
height: 1, // Set height to 1px to reduce padding
);
}
}

View File

@ -0,0 +1,156 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../../../utils/logic/PreferencesService.dart'; // For HapticFeedback
// ...Include PreferencesService and other necessary imports
class SetPinPopup extends StatefulWidget {
@override
_SetPinPopupState createState() => _SetPinPopupState();
}
class _SetPinPopupState extends State<SetPinPopup> {
String _pin = '';
String _confirmedPin = '';
bool _isSettingPin =
true; // True if setting the PIN, false if confirming the PIN
static const int pinLength = 4;
// Include the methods from PinInputScreen here
List<Widget> _buildNumberPad() {
List<int> numbers = List.generate(9, (index) => index + 1);
List<Widget> rows = [];
// First three rows (1-9)
for (var i = 0; i < 3; i++) {
rows.add(
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: numbers.skip(i * 3).take(3).map((number) {
return _buildNumberButton(number);
}).toList(),
),
);
}
// Last row with 0
rows.add(
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(width: 70), // Empty container to align 0 in center
_buildNumberButton(0),
Container(width: 70), // Empty container to align 0 in center
],
),
);
return rows;
}
Widget _buildNumberButton(int number) {
return GestureDetector(
onTap: () => _onNumberTap(number),
onTapDown: (_) =>
HapticFeedback.lightImpact(), // Cool haptic feedback on tap
child: Container(
width: 70,
height: 70,
margin: EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(color: Colors.black),
borderRadius: BorderRadius.circular(35),
),
child: Center(
child: Text(
number.toString(),
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
),
),
);
}
void _onNumberTap(int number) {
setState(() {
if (_isSettingPin) {
_pin += number.toString();
} else {
_confirmedPin += number.toString();
}
});
if ((_isSettingPin && _pin.length == pinLength) ||
(!_isSettingPin && _confirmedPin.length == pinLength)) {
if (!_isSettingPin && _pin == _confirmedPin) {
// Call setPin and enablePin methods, assuming they are async
PreferencesService().setPin(int.parse(_pin));
PreferencesService().enablePin();
Navigator.of(context).pop(); // Close the dialog
} else if (!_isSettingPin && _pin != _confirmedPin) {
// PINs don't match, reset and let the user set the PIN again
setState(() {
_pin = '';
_confirmedPin = '';
_isSettingPin = true; // Reset back to setting PIN
});
} else {
// Move to confirming the PIN
setState(() {
_isSettingPin = false;
});
}
}
}
@override
Widget build(BuildContext context) {
return Dialog(
backgroundColor: Colors.transparent,
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5),
child: Container(
height: 600,
width: 400,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(50))),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(_isSettingPin ? 'Set a PIN' : 'Repeat PIN',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
SizedBox(height: 20),
Row(
mainAxisSize: MainAxisSize.min,
children: List.generate(pinLength, (index) {
return Container(
margin: EdgeInsets.all(4),
width: 15,
height: 15,
decoration: BoxDecoration(
color:
(_isSettingPin ? _pin.length : _confirmedPin.length) >
index
? Colors.black
: Colors.transparent,
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(15),
),
);
}),
),
SizedBox(height: 20),
..._buildNumberPad(),
],
),
),
),
);
}
}

View File

@ -0,0 +1,64 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class TextSwitchContainer extends StatefulWidget {
final String leftText;
final String? rightText;
final bool hasSwitch;
final Function onTap;
final Function(bool)? onSwitchToggle;
TextSwitchContainer({
required this.leftText,
this.rightText,
this.hasSwitch = false,
required this.onTap,
this.onSwitchToggle,
});
@override
_TextSwitchContainerState createState() => _TextSwitchContainerState();
}
class _TextSwitchContainerState extends State<TextSwitchContainer> {
bool switchValue = false;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => widget.onTap(),
child: Container(
padding: EdgeInsets.symmetric(horizontal: 0, vertical: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
widget.leftText,
style: TextStyle(fontSize: 18),
),
if (widget.hasSwitch)
CupertinoSwitch(
trackColor: Colors.white,
thumbColor: switchValue ? Colors.lightGreen : Colors.grey,
value: switchValue,
onChanged: (newValue) {
setState(() {
switchValue = newValue;
widget.onSwitchToggle?.call(newValue);
});
},
activeColor: Colors.white, // Active (ON) color
// CupertinoSwitch doesn't allow inactive thumb color customization by default
// Track color changes with the active color
)
else if (widget.rightText != null)
Text(
widget.rightText!,
style: TextStyle(fontSize: 16, color: Colors.grey),
),
],
),
),
);
}
}

View File

@ -0,0 +1,184 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:go_router/go_router.dart';
import '../../utils/logic/PreferencesService.dart'; // For Haptic Feedback
class StartPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'PIN Input',
home: PinInputScreen(),
);
}
}
class PinInputScreen extends StatefulWidget {
@override
_PinInputScreenState createState() => _PinInputScreenState();
}
class _PinInputScreenState extends State<PinInputScreen> {
String _pin = '';
static const int pinLength = 4;
@override
void initState() {
super.initState();
_checkPinStatus();
}
Future<void> _checkPinStatus() async {
bool pinEnabled = await PreferencesService()
.isPinEnabled(); // Adjust with actual implementation
if (!pinEnabled) {
// If no PIN is enabled, navigate to the "/first" route.
// Navigator context will be available after the widget build.
Future.microtask(() => context.go("/first"));
}
}
// This method would be your actual method for checking the pin
// Replace with your actual method from PreferencesService
Future<bool> checkPin(int pin) async {
bool pinRight = await PreferencesService().checkPin(pin);
return pinRight;
}
void _onNumberTap(int number) {
if (_pin.length < pinLength) {
setState(() {
_pin += number.toString();
});
if (_pin.length == pinLength) {
_validatePin();
}
}
}
Future<void> _validatePin() async {
bool correct = await checkPin(int.parse(_pin));
if (correct) {
print("Right");
context.go("/first");
// Perform any actions you need on successful pin entry
} else {
// Handle wrong pin entry, e.g., reset pin, show error, etc.
print("Wrong PIN");
}
// Resetting the PIN for this example, you might want to do this differently
setState(() {
_pin = '';
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey.shade200, // Light gray background
body: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.grey, Colors.red.shade100],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
child: Center(
child: Container(
height: 600,
width: 400,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(50))),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Welcome Back!',
style:
TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
SizedBox(height: 20),
Row(
mainAxisSize: MainAxisSize.min,
children: List.generate(pinLength, (index) {
return Container(
margin: EdgeInsets.all(4),
width: 15,
height: 15,
decoration: BoxDecoration(
color: _pin.length > index
? Colors.black
: Colors.transparent,
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(15),
),
);
}),
),
SizedBox(height: 20),
..._buildNumberPad(),
],
),
),
),
),
);
}
List<Widget> _buildNumberPad() {
List<int> numbers = List.generate(9, (index) => index + 1);
List<Widget> rows = [];
// First three rows (1-9)
for (var i = 0; i < 3; i++) {
rows.add(
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: numbers.skip(i * 3).take(3).map((number) {
return _buildNumberButton(number);
}).toList(),
),
);
}
// Last row with 0
rows.add(
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(width: 70), // Empty container to align 0 in center
_buildNumberButton(0),
Container(width: 70), // Empty container to align 0 in center
],
),
);
return rows;
}
Widget _buildNumberButton(int number) {
return GestureDetector(
onTap: () => _onNumberTap(number),
onTapDown: (_) =>
HapticFeedback.lightImpact(), // Cool haptic feedback on tap
child: Container(
width: 70,
height: 70,
margin: EdgeInsets.all(10),
decoration: BoxDecoration(
border: Border.all(color: Colors.black),
borderRadius: BorderRadius.circular(35),
),
child: Center(
child: Text(
number.toString(),
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
),
),
);
}
}

View File

@ -21,25 +21,24 @@ class StatisticPage extends StatelessWidget {
"browse your memories!",
style: TextStyle(fontSize: 24),
),
Container(
height: 650,
Expanded(
child: SingleChildScrollView(
child: Column(
children: [
Container(
child: CalendarWidget(currentDate: DateTime.now())),
Container(
child: CalendarWidget(
currentDate: DateTime(
DateTime.now().year,
DateTime.now().month - 1,
DateTime.now().day))),
Container(
child: CalendarWidget(
currentDate: DateTime(
DateTime.now().year,
DateTime.now().month - 2,
DateTime.now().day))),
DateTime.now().day - 3))),
Container(
child: CalendarWidget(
currentDate: DateTime(
DateTime.now().year,
DateTime.now().month - 1,
DateTime.now().day - 1))),
Container(
child: CalendarWidget(currentDate: DateTime.now())),
SizedBox(
height: 50,
)

View File

@ -1,9 +1,10 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:intl/intl.dart';
import '../../../utils/definitions/ColorPairs.dart';
import '../../../utils/logic/PreferencesService.dart'; // Correct the path as necessary
class CalendarWidget extends StatefulWidget {
final DateTime currentDate;
@ -14,12 +15,32 @@ class CalendarWidget extends StatefulWidget {
}
class _CalendarWidgetState extends State<CalendarWidget> {
late List<DateTime> dateList;
List<DateTime> dateList = []; // Initialized immediately as an empty list
Map<String, DiaryEntry?> diaryEntries = {}; // Holds diary entries by date
late ColorPair currentColorPair; // Holds the current color pair
@override
void initState() {
super.initState();
dateList = _generateDateList(widget.currentDate);
dateList = _generateDateList(widget.currentDate); // Ensured initialization
_loadColorPairAndDiaryData();
}
void _loadColorPairAndDiaryData() async {
// Load color pair
currentColorPair =
await PreferencesService().loadColorPair() ?? colorPairs[0];
// Generate date list
//dateList = _generateDateList(widget.currentDate);
// Load diary entries for each date
for (DateTime date in dateList) {
DiaryEntry? entry = await PreferencesService().getDiaryEntryByDate(date);
String key = DateFormat('yyyy-MM-dd').format(date);
diaryEntries[key] = entry;
}
setState(() {}); // Update the state to reflect new data
}
List<DateTime> _generateDateList(DateTime date) {
@ -52,64 +73,126 @@ class _CalendarWidgetState extends State<CalendarWidget> {
@override
Widget build(BuildContext context) {
print(dateList.length);
double addon = dateList.length > 35 ? 50 : 0;
return Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Colors.black.withOpacity(0.3),
style: BorderStyle.solid,
width: 2))),
height: 300 + addon,
child: Padding(
padding: const EdgeInsets.fromLTRB(25, 0, 25, 0),
child: GridView.builder(
physics:
NeverScrollableScrollPhysics(), // Disable scrolling for GridView
String monthName =
DateFormat("MMMM yyyy").format(widget.currentDate); // For month header
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 7,
),
itemBuilder: (context, index) {
DateTime date = dateList[index];
bool isCurrentMonth = date.month == widget.currentDate.month;
double outerRadius = 25; // Fixed outer radius
double innerRadius = Random().nextDouble() * outerRadius;
double opacity = isCurrentMonth ? 1.0 : 0.15;
return FutureBuilder<ColorPair>(
future: PreferencesService().loadColorPair(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
}
ColorPair colorPair = snapshot.data!;
return Center(
child: GestureDetector(
onTap: () => {
context.go('/entry', extra: date)
}, // Using the callback here
child: Container(
width: outerRadius * 2,
height: outerRadius * 2,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(color: Colors.black, width: 2),
color: isCurrentMonth
? Colors.transparent
: Colors.grey.withOpacity(opacity),
),
child: Stack(
alignment: Alignment.center,
children: [
CircleWidget(
radius: innerRadius,
color: Colors.lightGreenAccent.withOpacity(0.75)),
Text(
DateFormat("d").format(date),
style: TextStyle(color: Colors.black),
return Container(
height: 360 + addon,
child: Column(
children: [
Padding(
padding: EdgeInsets.symmetric(vertical: 8.0),
child: Text(monthName,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold)), // Month Header
),
Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(25, 0, 25, 0),
child: GridView.builder(
physics: NeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 7,
),
],
itemBuilder: (context, index) {
DateTime date = dateList[index];
bool isCurrentMonth =
date.month == widget.currentDate.month;
double opacity = isCurrentMonth ? 1.0 : 0.15;
// Fetching diary entry percentage value
DiaryEntry? entry =
diaryEntries[DateFormat('yyyy-MM-dd').format(date)];
double fillPercentage =
entry != null ? entry.percentValue / 100.0 : 0.0;
return Center(
child: GestureDetector(
onTap: () => {context.go('/entry', extra: date)},
child: Container(
width: 50,
height: 50,
decoration: BoxDecoration(
shape: BoxShape.circle,
border:
Border.all(color: Colors.black, width: 2),
color: DateTime.now().day == date.day &&
DateTime.now().month == date.month &&
DateTime.now().year == date.year
? Colors
.orange // Setting background color for current day
: isCurrentMonth
? Colors.transparent
: Colors.grey.withOpacity(opacity),
),
child: Stack(
alignment: Alignment.center,
children: [
CircleWidget(
radius: 25 * fillPercentage,
color: fillPercentage > 0
? colorPair.backgroundColor
: Colors.teal.withOpacity(opacity)),
Text(
DateFormat("d").format(date),
style: TextStyle(color: Colors.black),
),
],
),
),
),
);
},
itemCount: dateList.length,
),
),
),
),
);
},
itemCount: dateList.length,
],
),
);
});
}
Widget _buildDateCell(DateTime date, DiaryEntry? entry) {
bool isCurrentMonth = date.month == widget.currentDate.month;
double opacity = isCurrentMonth ? 1.0 : 0.5;
Color bgColor = entry != null
? currentColorPair.backgroundColor.withOpacity(opacity)
: Colors.transparent;
double fillPercentage = entry != null
? entry.percentValue / 100
: 0; // Assuming percentValue is 0-100
return GestureDetector(
onTap: () {
context.go('/entry', extra: date);
},
child: Container(
// ... existing decoration adjusted for opacity ...
child: Stack(
alignment: Alignment.center,
children: [
// Inner circle representing diary entry value
CircleWidget(
radius: 25 * fillPercentage, // Adjust radius based on value
color: fillPercentage > 0
? currentColorPair.backgroundColor
: Colors.grey.withOpacity(opacity)),
Text(
DateFormat("d").format(date),
style: TextStyle(color: Colors.black),
),
],
),
),
);

View File

@ -58,7 +58,7 @@ class _WritePageState extends State<WritePage> {
// Background circle
Positioned.fill(
child: CustomPaint(
painter: CirclePainter(_sliderValue),
painter: CirclePainter(_sliderValue, Colors.yellowAccent),
),
),
// Main content
@ -70,13 +70,16 @@ class _WritePageState extends State<WritePage> {
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
QuestionSliderWidget(
onSliderPositionChanged: (value) {
print("sliderposchanged");
},
date: DateTime.now(),
questionText: "How are you today",
initialSliderValue: 0,
isSliderEnabled: true,
onSliderChanged: (value) {
setState(() {
_sliderValue = value;
// _sliderValue = value;
});
}),
TextButton(