Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SettingsScreen #21

Merged
merged 8 commits into from
Oct 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions assets/secrets.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

# Client secrets
CLIENT_ID=
CLIENT_SECRET=
REDIRECT_URI=

# GetSongBPM API Key
Expand Down
29 changes: 22 additions & 7 deletions lib/config.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import 'dart:developer';

import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:shared_preferences/shared_preferences.dart';

// Config stores the general global variables
class Config {
// Private
static final String _firstRunFlag = "first_run";
static final String _darkModeFlag = "dark_mode";

static late SharedPreferences prefs;

// Public
static String get clientId {
Expand All @@ -22,19 +26,30 @@ class Config {
fallback: "read_song_bpm_api_err");
}

// getFirstRunFlag : Checks if the user has opened the app before or not.
// If the user is running the app for the first time, return true, else false.
static Future<bool> getFirstRunFlag() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
static bool get firstRunFlag {
return prefs.getBool(_firstRunFlag) ?? true;
}

// setFirstRunFlag : Sets the first run flag to required value.
static Future<void> setFirstRunFlag(bool flag) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
static set firstRunFlag(bool flag) {
prefs.setBool(_firstRunFlag, flag);
}

static bool get isDarkMode {
return prefs.getBool(_darkModeFlag) ?? false;
}

static set isDarkMode(bool flag) {
prefs.setBool(_darkModeFlag, flag);
}

static ThemeData getTheme() {
var theme = (isDarkMode) ? ThemeData.dark() : ThemeData.light();
return theme.copyWith(
pageTransitionsTheme: PageTransitionsTheme(builders: {
TargetPlatform.android: ZoomPageTransitionsBuilder(),
}));
}

// loadSecrets : Attempts to load the client ID and redirect URI associated
// with this application.
static Future<void> loadSecrets() async {
Expand Down
14 changes: 8 additions & 6 deletions lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import 'package:catch_my_cadence/config.dart';
import 'package:catch_my_cadence/routes.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';

// App starts running from the main activity.
void main() {
Future<void> main() async {
// Do this so we can load preferences here.
WidgetsFlutterBinding.ensureInitialized();
Config.prefs = await SharedPreferences.getInstance();

runApp(MaterialApp(
theme: ThemeData.dark().copyWith(
pageTransitionsTheme: PageTransitionsTheme(builders: {
TargetPlatform.android: ZoomPageTransitionsBuilder(),
}),
),
theme: Config.getTheme(),
title: "Catch My Cadence",
// App always shows the loading screen when open.
initialRoute: RouteDelegator.LOADING_SCREEN_ROUTE,
Expand Down
7 changes: 7 additions & 0 deletions lib/routes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:catch_my_cadence/screens/about_screen.dart';
import 'package:catch_my_cadence/screens/confirm_connection_screen.dart';
import 'package:catch_my_cadence/screens/loading_screen.dart';
import 'package:catch_my_cadence/screens/main_screen.dart';
import 'package:catch_my_cadence/screens/settings_screen.dart';
import 'package:flutter/material.dart';

// RouteDelegator helps to navigate the different screens of the app.
Expand All @@ -12,6 +13,7 @@ class RouteDelegator {
static const String MAIN_SCREEN_ROUTE = 'main';
static const String CONFIRM_CONNECTION_SCREEN_ROUTE = 'confirm_connect';
static const String ABOUT_SCREEN_ROUTE = 'about';
static const String SETTINGS_SCREEN_ROUTE = 'settings';

static Route<dynamic> delegateRoute(RouteSettings settings) {
switch (settings.name) {
Expand All @@ -35,6 +37,11 @@ class RouteDelegator {
return MaterialPageRoute(
settings: RouteSettings(name: ABOUT_SCREEN_ROUTE),
builder: (ctx) => AboutScreen());
case SETTINGS_SCREEN_ROUTE: // Settings screen
log("Pushing SettingsScreen...");
return MaterialPageRoute(
settings: RouteSettings(name: SETTINGS_SCREEN_ROUTE),
builder: (ctx) => SettingsScreen());
default: // Unexpected error screen
log("Pushing ErrorScreen...");
return MaterialPageRoute(
Expand Down
2 changes: 1 addition & 1 deletion lib/screens/about_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class AboutScreen extends StatelessWidget {
Widget build(BuildContext ctx) {
return Scaffold(
appBar: AppBar(
// Back arrow that returns to MainScreen
// Back arrow that returns to MainScreen.
leading: IconButton(
onPressed: () {
Navigator.of(ctx).pop();
Expand Down
2 changes: 1 addition & 1 deletion lib/screens/confirm_connection_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class ConfirmConnectionScreen extends StatelessWidget {
Future<void> confirmSpotifyLinkage(BuildContext ctx) async {
// Set first run flag to false.
log("Setting first run flag to false...");
await Config.setFirstRunFlag(false);
Config.firstRunFlag = false;

// Navigate to main screen.
Navigator.of(ctx).pushReplacementNamed(RouteDelegator.MAIN_SCREEN_ROUTE);
Expand Down
2 changes: 1 addition & 1 deletion lib/screens/loading_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class _LoadingScreenState extends State<LoadingScreen>
if (!(await Permission.activityRecognition.isGranted)) {
_requestActivityRecognitionPermission();
} else {
bool firstRun = await Config.getFirstRunFlag();
bool firstRun = Config.firstRunFlag;
log("First run status: $firstRun");

// Navigate to required screen based on flag.
Expand Down
59 changes: 59 additions & 0 deletions lib/screens/settings_screen.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import 'package:catch_my_cadence/config.dart';
import 'package:flutter/material.dart';

// SettingsScreen allows user to change toggle dark theme within the app
// It is accessible via side menu options.
class SettingsScreen extends StatefulWidget {
SettingsScreen({Key? key}) : super(key: key);

@override
_SettingsScreenState createState() => _SettingsScreenState();
}

class _SettingsScreenState extends State<SettingsScreen> {
late bool _isDarkMode;

@override
void initState() {
super.initState();
_isDarkMode = Config.isDarkMode;
}

@override
Widget build(BuildContext ctx) {
return Scaffold(
appBar: AppBar(
// Back arrow that returns to MainScreen.
leading: IconButton(
onPressed: () {
Navigator.of(ctx).pop();
},
icon: Icon(Icons.arrow_back)),
title: Text("Settings Screen"),
),
// theme_button_widget
body: Padding(
padding: EdgeInsets.all(10),
child: options(),
),
);
}

ListView options() {
return ListView(
children: [
// Change theme option
SwitchListTile.adaptive(
value: _isDarkMode,
title: Text("Dark Mode"),
subtitle: Text("Restart the app to view changes."),
onChanged: (val) {
Config.isDarkMode = val;
setState(() {
_isDarkMode = val;
});
})
],
);
}
}
48 changes: 37 additions & 11 deletions lib/screens/widgets/side_menu_widget.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import 'dart:developer';

import 'package:catch_my_cadence/config.dart';
import 'package:catch_my_cadence/routes.dart';
import 'package:flutter/material.dart';

Expand All @@ -23,6 +26,7 @@ class SideMenu extends StatelessWidget {
_aboutOption(context),
_helpOption(context),
_settingsOption(context),
_logoutOption(context)
]),
);
}
Expand Down Expand Up @@ -86,7 +90,7 @@ class SideMenu extends StatelessWidget {
Icons.info,
),
onTap: () {
// Pops all screens from view stack until AboutScreen is at the top of the stack.
// Pop SideMenu and push AboutScreen.
Navigator.of(context)
.popAndPushNamed(RouteDelegator.ABOUT_SCREEN_ROUTE);
});
Expand All @@ -111,15 +115,37 @@ class SideMenu extends StatelessWidget {
// _settingsOption : Routes user to SettingsScreen.
ListTile _settingsOption(BuildContext context) {
return ListTile(
title: Text(
'Settings',
),
leading: Icon(
Icons.settings,
),
// onTap:(){
// TODO: Link to settings screen
// }
);
title: Text(
'Settings',
),
leading: Icon(
Icons.settings,
),
onTap: () {
// Pop SideMenu and push SettingsScreen.
Navigator.of(context)
.popAndPushNamed(RouteDelegator.SETTINGS_SCREEN_ROUTE);
});
}

// _LogoutOption : Routes the user to LoggedOutScreen.
ListTile _logoutOption(BuildContext context) {
return ListTile(
title: Text(
'Log Out',
),
leading: Icon(
Icons.logout,
),
onTap: () {
// Sets firstRunFlag to true.
Config.firstRunFlag = true;
log("Logging Out");
// "Restart" app by removing all screens from the stack
// and loading the LoadingScreen again.
Navigator.of(context).pushNamedAndRemoveUntil(
RouteDelegator.LOADING_SCREEN_ROUTE,
(Route<dynamic> route) => false);
});
}
}