diff --git a/lib/main.dart b/lib/main.dart index 28f75ef..b1479e8 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -5,15 +5,17 @@ import 'package:provider/provider.dart'; // * Prefs import import 'package:soil_moisture_app/prefs/user_prefs.dart'; -import 'package:soil_moisture_app/states/selected_card_state.dart'; // * State import +import 'package:soil_moisture_app/states/selected_card_state.dart'; import 'package:soil_moisture_app/states/theme_state.dart'; // * ui import +import 'package:soil_moisture_app/ui/build_theme.dart'; import 'package:soil_moisture_app/ui/options.dart'; // * utils import +import 'package:soil_moisture_app/utils/app_info.dart'; import 'package:soil_moisture_app/utils/sizes.dart'; // * Pages Import @@ -21,9 +23,9 @@ import 'pages/Analysis.dart'; import 'pages/Overview.dart'; void main() async { - WidgetsFlutterBinding - .ensureInitialized(); //for awaiting to load sharedPreferences + WidgetsFlutterBinding.ensureInitialized(); //for awaiting await loadPrefs(); + await loadAppInfo(); runApp( MultiProvider( providers: [ @@ -43,70 +45,110 @@ class Root extends StatelessWidget { final String title = 'Soif'; @override Widget build(BuildContext context) { + var _themeState = Provider.of(context); return MaterialApp( title: title, debugShowCheckedModeBanner: false, - home: DefaultTabController(length: 2, child: Home()), - theme: Provider.of(context).getTheme, + home: Home(), + theme: buildLightTheme(), + darkTheme: buildDarkTheme(), + themeMode: determineThemeMode(_themeState.isDarkTheme), ); } } -class Home extends StatelessWidget { - final List _children = [ +class Home extends StatefulWidget { + final List _tabPages = [ Overview(), Analysis(), ]; + @override + @override + _HomeState createState() => _HomeState(); +} + +class _HomeState extends State with SingleTickerProviderStateMixin { + TabController _controller; + void initState() { + super.initState(); + _controller = TabController( + vsync: this, + initialIndex: 0, + length: widget._tabPages.length, + ); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + Future _popScopeInvoke() async { + if (this._controller.index == 1) { + this._controller.index = 0; + return Future.value(false); + } else { + await getPrefs.setBool( + 'isDarkTheme', Provider.of(context).isDarkTheme); + return Future.value(true); + } + } @override Widget build(BuildContext context) { print(appWidth(context)); - return Scaffold( - appBar: AppBar( - title: Image.asset( - (Provider.of(context).isDarkTheme) - ? './assets/images/Soif_sk_dark.png' - : './assets/images/Soif_sk.png', - height: appWidth(context) * 0.08, + return WillPopScope( + onWillPop: _popScopeInvoke, + child: Scaffold( + appBar: AppBar( + title: Image.asset( + (Provider.of(context).isDarkTheme) + ? './assets/images/Soif_sk_dark.png' + : './assets/images/Soif_sk.png', + height: appWidth(context) * 0.08, + ), + centerTitle: true, ), - centerTitle: true, - ), - body: TabBarView( - children: _children, - physics: NeverScrollableScrollPhysics(), - ), - bottomNavigationBar: Container( - color: Theme.of(context).accentColor, - child: Row( - children: [ - Flexible( - child: TabBar( - tabs: [ - Tab( - icon: Icon( - Icons.remove_red_eye, + body: TabBarView( + controller: this._controller, + children: widget._tabPages, + physics: NeverScrollableScrollPhysics(), + ), + bottomNavigationBar: Container( + color: Theme.of(context).accentColor, + child: Row( + children: [ + Flexible( + child: TabBar( + controller: this._controller, + tabs: [ + Tab( + icon: Icon( + Icons.remove_red_eye, + ), + text: 'Overview', ), - text: 'Overview', + Tab( + icon: Icon( + Icons.timeline, + ), + text: 'Analysis', + ) + ], + unselectedLabelStyle: TextStyle( + fontSize: appWidth(context) * 0.025, + fontFamily: 'Ocrb', + ), + labelStyle: TextStyle( + fontSize: appWidth(context) * 0.035, + fontFamily: 'Ocrb', ), - Tab( - icon: Icon( - Icons.timeline, - ), - text: 'Analysis', - ) - ], - unselectedLabelStyle: TextStyle( - fontSize: appWidth(context) * 0.025, - fontFamily: 'Ocrb', - ), - labelStyle: TextStyle( - fontSize: appWidth(context) * 0.035, - fontFamily: 'Ocrb', ), ), - ), - Options() - ], + Options() + ], + ), ), ), ); diff --git a/lib/pages/Credits.dart b/lib/pages/Credits.dart index c6b32e3..b1d399e 100644 --- a/lib/pages/Credits.dart +++ b/lib/pages/Credits.dart @@ -21,6 +21,7 @@ import 'package:url_launcher/url_launcher.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; // * utils import +import 'package:soil_moisture_app/utils/app_info.dart'; import 'package:soil_moisture_app/utils/sizes.dart'; // * data import @@ -119,20 +120,36 @@ class _CreditsState extends State { centerTitle: true, ), body: SafeArea( - minimum: EdgeInsets.symmetric(horizontal: appWidth(context) * 0.03), child: ListView( + padding: EdgeInsets.all(appWidth(context) * 0.03), physics: AlwaysScrollableScrollPhysics(parent: BouncingScrollPhysics()), children: [ - SizedBox( - height: appWidth(context) * 0.03, - ), Image.asset( (Provider.of(context).isDarkTheme) ? './assets/images/Soif_sk_dark.png' : './assets/images/Soif_sk.png', height: appWidth(context) * 0.25, ), + Padding( + padding: EdgeInsets.all(appHeight(context) * 0.01), + child: RichText( + textAlign: TextAlign.center, + strutStyle: StrutStyle(fontSize: 14.0), + text: TextSpan( + style: Theme.of(context).textTheme.body2.copyWith( + color: (Provider.of(context).isDarkTheme) + ? subtleWhiteTextColor + : subtleBlackTextColor, + ), + children: [ + TextSpan(text: 'Version ${getAppInfo.version}\n'), + TextSpan(text: 'Built with '), + WidgetSpan(child: FlutterLogo()), + ], + ), + ), + ), Padding( padding: EdgeInsets.symmetric(vertical: appWidth(context) * 0.03), child: Text( @@ -189,9 +206,6 @@ class _CreditsState extends State { onTap: () => _launchUrl(apiUrl), ), ), - SizedBox( - height: appWidth(context) * 0.03, - ) ], ), ), diff --git a/lib/states/theme_state.dart b/lib/states/theme_state.dart index 95ce624..e98565d 100644 --- a/lib/states/theme_state.dart +++ b/lib/states/theme_state.dart @@ -3,38 +3,18 @@ import 'package:flutter/services.dart'; // * Prefs Import import 'package:soil_moisture_app/prefs/user_prefs.dart'; - -// * ui import import 'package:soil_moisture_app/ui/build_theme.dart'; class ThemeState with ChangeNotifier { - ThemeData _appTheme; - SystemUiOverlayStyle _systemUiTheme; - bool isDarkTheme = getPrefs.getBool('isDarkTheme') ?? false; + bool isDarkTheme; ThemeState() { - _appTheme = (isDarkTheme) ? buildDarkTheme() : buildLightTheme(); - _systemUiTheme = (isDarkTheme) ? buildDarkSystemUi() : buildLightSystemUi(); - SystemChrome.setSystemUIOverlayStyle(_systemUiTheme); + isDarkTheme = getPrefs.getBool('isDarkTheme') ?? false; + SystemChrome.setSystemUIOverlayStyle(appSystemUiTheme(isDarkTheme)); } - ThemeData get getTheme => _appTheme; - SystemUiOverlayStyle get getSystemUiTheme => _systemUiTheme; - void toggleTheme() { - if (isDarkTheme) { - _appTheme = buildLightTheme(); - _systemUiTheme = buildLightSystemUi(); - } else { - _appTheme = buildDarkTheme(); - _systemUiTheme = buildDarkSystemUi(); - } isDarkTheme = !isDarkTheme; - SystemChrome.setSystemUIOverlayStyle(_systemUiTheme); + SystemChrome.setSystemUIOverlayStyle(appSystemUiTheme(isDarkTheme)); notifyListeners(); - updateThemePrefs(); - } - - void updateThemePrefs() async { - await getPrefs.setBool('isDarkTheme', isDarkTheme); } } diff --git a/lib/ui/build_theme.dart b/lib/ui/build_theme.dart index 52e230e..b913d93 100644 --- a/lib/ui/build_theme.dart +++ b/lib/ui/build_theme.dart @@ -10,6 +10,15 @@ import 'package:flutter/services.dart'; // * ui import import 'package:soil_moisture_app/ui/colors.dart'; +// * Helper functions for returning appropriate themes +ThemeMode determineThemeMode(bool isDarkTheme) { + return isDarkTheme ? ThemeMode.dark : ThemeMode.light; +} + +SystemUiOverlayStyle appSystemUiTheme(bool isDarkTheme) { + return isDarkTheme ? buildDarkSystemUi() : buildLightSystemUi(); +} + // * Text theme to be used throughout the app TextTheme appTextTheme = TextTheme( display4: TextStyle(fontFamily: 'Ocrb'), diff --git a/lib/ui/options.dart b/lib/ui/options.dart index 376d985..dcf312c 100644 --- a/lib/ui/options.dart +++ b/lib/ui/options.dart @@ -1,7 +1,7 @@ /* * options -* Displays a bottom options menu for 'Pump Threshold Control' and 'About', for now. +* Displays a bottom options menu for 'Dark Theme', 'Pump Threshold Control' and 'About'. */ import 'package:flutter/material.dart'; @@ -22,6 +22,7 @@ import 'package:soil_moisture_app/states/theme_state.dart'; class Options extends StatelessWidget { void showOptions(BuildContext context) { + var _themeState = Provider.of(context); showModalBottomSheet( isScrollControlled: true, context: context, @@ -29,15 +30,15 @@ class Options extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ ListTile( - leading: (Provider.of(context).isDarkTheme) + leading: (_themeState.isDarkTheme) ? Icon(FontAwesomeIcons.solidLightbulb) : Icon(FontAwesomeIcons.lightbulb), title: Text('Dark Theme'), trailing: Switch( - value: Provider.of(context).isDarkTheme, - onChanged: (_) => Provider.of(context).toggleTheme(), + value: _themeState.isDarkTheme, + onChanged: (_) => _themeState.toggleTheme(), ), - onTap: () => Provider.of(context).toggleTheme(), + onTap: () => _themeState.toggleTheme(), ), ListTile( leading: Icon(FontAwesomeIcons.slidersH), diff --git a/lib/utils/app_info.dart b/lib/utils/app_info.dart new file mode 100644 index 0000000..b236ab6 --- /dev/null +++ b/lib/utils/app_info.dart @@ -0,0 +1,15 @@ +/* + * app_info + * + * Fetches the app properties from the system. + */ + +import 'package:package_info/package_info.dart'; + +PackageInfo _appInfo; + +loadAppInfo() async { + _appInfo = await PackageInfo.fromPlatform(); +} + +PackageInfo get getAppInfo => _appInfo; diff --git a/pubspec.lock b/pubspec.lock index 2f7a402..b050ef8 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -135,6 +135,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.8" + package_info: + dependency: "direct main" + description: + name: package_info + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.0+13" path: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index ac95992..aa83dc4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: soil_moisture_app description: A new Flutter project. -version: 1.0.2+10002 +version: 1.0.3+10003 environment: sdk: ">=2.2.2 <3.0.0" @@ -16,6 +16,7 @@ dependencies: flutter_image: ^3.0.0 provider: ^3.1.0+1 shared_preferences: ^0.5.6 + package_info: ^0.4.0+13 cupertino_icons: ^0.1.3 http: