I am implementing ThemeMode feature on my fluter app for example if user have saved ThemeMode (light, dark or system) saved in SharedPreferences then it should load on app startup otherwise bydefault it is light. so i am using Provider for state management.
Here is my approach:
on themes_provider.dart
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../utils/themes.dart';
class ThemesProvider extends ChangeNotifier {
ThemeMode? _themeMode;
ThemeMode? get themeMode => _themeMode;
ThemeData get lightTheme => ThemeManager().lightTheme;
ThemeData get darkTheme => ThemeManager().darkTheme;
void initThemeMode() async {
final _prefs = await SharedPreferences.getInstance();
final _currentTheme = _prefs.getString('theme') ?? 'light';
if (_currentTheme == 'light') {
_themeMode = ThemeMode.light;
} else if (_currentTheme == 'dark') {
_themeMode = ThemeMode.dark;
} else {
_themeMode = ThemeMode.system;
}
notifyListeners();
}
void setThemeMode(int value) async {
final _prefs = await SharedPreferences.getInstance();
_prefs.setInt('theme', value);
notifyListeners();
}
}
and then on main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => UserProvider()),
ChangeNotifierProvider(
create: (context) => ThemesProvider(),
)
],
builder: (context, snapshot) {
final _themeNotifier =
Provider.of<ThemesProvider>(context, listen: false);
_themeNotifier.initThemeMode();
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: _themeNotifier.lightTheme,
darkTheme: _themeNotifier.darkTheme,
themeMode: _themeNotifier.themeMode,
home: HomeScreen(),
onGenerateRoute: RouteGenerator.generateRoute,
initialRoute: RouteGenerator.loginRoute,
);
});
}
}
But in debug console i am getting null value on _themeNotifier.themeMode
please help.
Related
Writing on the page can not be converted to the page display the error message
... .... flutter ... ... dart
.......... application restart ..................
my code...
main
Future<void> main() async {
runZonedGuarded(() {
WidgetsFlutterBinding.ensureInitialized();
FlutterError.onError = (FlutterErrorDetails errorDetails) {
print('This is an error on the Flutter SDK');
// print(errorDetails.exception);
print('-----');
// print(errorDetails.stack);
};
runApp(const MyApp());
}, (error, stackTrace) {
print('This is a pure Dart error');
// print(error);
print('-----');
// print(stackTrace);
});
}
myApp
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
builder: (BuildContext context, Widget? widget) {
ErrorWidget.builder =
(FlutterErrorDetails errorDetails) => const ErrorPage();
return widget!;
},
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
debugShowCheckedModeBanner: false,
home: const MyApp());
}
}
Please I want to know how to launch WhatsApp in the Flutter webview app or launch WhatsApp from the browser in Flutter, have used many codes with no errors but they do not work.Am using mac m1
and vscode
import 'package:coinpaga/Connectivity_Provider.dart';
import 'package:coinpaga/services/local_notification_service.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'homepage.dart';
import 'package:hexcolor/hexcolor.dart';
import 'package:colorful_safe_area/colorful_safe_area.dart';
/// Receive message when app is in background solution for on
message
Future<void> backgroundHandler(RemoteMessage message)async{
print(message.data.toString());
print(message.notification!.title);
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
LocalNotificationServices.initialize();
await Firebase.initializeApp();
FirebaseMessaging.onBackgroundMessage(backgroundHandler);
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (context) => ConnectivityProvider(),
child: HomePage(),
)
],
child:MaterialApp(
title: 'coinpaga',
theme: ThemeData(
primarySwatch: Colors.blue,
),
debugShowCheckedModeBanner: false,
home: ColorfulSafeArea(
color: HexColor("#2e2a42"),
top: true,
bottom: false,
left: false,
right: false,
child: HomePage(),
)
),
);
}
}
Home.dart
import 'package:coinpaga/Connectivity_Provider.dart';
import 'package:coinpaga/no_internet.dart';
import 'package:flutter/material.dart';
import 'package:flutter_webview_plugin/flutter_webview_plugin.dart';
import 'package:provider/provider.dart';
class HomePage extends StatefulWidget {
// ignore: unused_field
final _flutterwebview = FlutterWebviewPlugin();
HomePage({ Key? key }) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
void initState() {
super.initState();
Provider.of<ConnectivityProvider>(context, listen:
false).startMonitoring();
}
#override
Widget build(BuildContext context) {
return pageUI();
}
#override
void dispose() {
_flutterwebview.dispose();
super.dispose();
}
}
Widget pageUI() {
return Consumer<ConnectivityProvider>(
builder: (context, model, child) {
return model.isOnline
? WebviewScaffold(
url: 'https://coinpaga.com',
withLocalStorage: true,
withJavascript: true,
scrollBar: false,
initialChild: Center(child: Text('Loading...')),
) : NoInternet();
},
);
}
// ignore: camel_case_types
class _flutterwebview {
static void dispose() {}
}
Please help me go through it.
String text = "Hello World !! Hey There";
String url = "https://wa.me/?text=${Uri.encodeFull(text)}";
if (await canLaunchUrl(Uri.parse(url))) {
await launchUrl(Uri.parse(url),
mode: LaunchMode.externalApplication);
}
First, add url_launcher, then I use this code to launch whatsapp on flutter webview and works.
WebView(
initialUrl: getUrl(_url),
javascriptMode: JavascriptMode.unrestricted,
navigationDelegate: (NavigationRequest request) async {
if (request.url
.startsWith('https://api.whatsapp.com/send?phone')) {
print('blocking navigation to $request}');
List<String> urlSplitted = request.url.split("&text=");
String phone = "0123456789";
String message =
urlSplitted.last.toString().replaceAll("%20", " ");
await _launchURL(
"https://wa.me/$phone/?text=${Uri.parse(message)}");
return NavigationDecision.prevent;
}
print('allowing navigation to $request');
return NavigationDecision.navigate;
},
)
_launchURL(String url) async {
if (await canLaunch(url)) {
await launch(url);
} else {
throw 'Could not launch $url';
}
}
You can use url_launcher to launch URLs.
You can give https://api.whatsapp.com/send/?phone=(phone_number) URL to launch.
For the launching the WhatsApp Website use launch('https://api.whatsapp.com/send/?phone=(phone_number)')
Make sure you give your country code without (+).
I wanted to make my application to work in 2 different language.
I guess problem is that I cannot handle Cosumer in multiprovider ,could it be?
I am using flutter_localization package alongside with flutter provider package;
Here is my code:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
AppLanguage appLanguage = AppLanguage();
await appLanguage.fetchLocale();
SharedPreferences.getInstance().then((prefs) {
var darkModeOn = prefs.getBool('darkMode') ?? true;
runApp(
ChangeNotifierProvider<ThemeManager>(
builder: (_) => ThemeManager(lightTheme),
child: MyApp(appLanguage: appLanguage),
),
);
});
}
Class My App
class MyApp extends StatelessWidget {
final AppLanguage appLanguage;
MyApp({this.appLanguage});
#override
Widget build(BuildContext context) {
final themeNotifier = Provider.of<ThemeManager>(context);
return MultiProvider(
providers: [
ChangeNotifierProvider.value(value: ApiService()),
ChangeNotifierProxyProvider<ApiService, StudentData>(
builder: (ctx, auth, _) => StudentData(auth.token, auth.school),
)
],
child: Consumer<ApiService>(
builder: (ctx, auth, _) => ChangeNotifierProvider<AppLanguage>(
builder: (_) => appLanguage,
child: Consumer<AppLanguage>(
builder: (context, model, child) => MaterialApp(
locale: appLanguage.appLocal,
supportedLocales: [Locale('ru', 'RU'), Locale('uz', 'UZ')],
localizationsDelegates: [
AppLocalization.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
debugShowCheckedModeBanner: false,
title: "Socratus |Mobile",
theme: themeNotifier.getTheme(),
home: auth.isAuth
? MainScreen()
: FutureBuilder(
future: auth.tryAutoLogin(),
builder: (ctx, authResultSnapshot) => LoginScreen()),
routes: {
MainScreen.routeName: (ctx) => MainScreen(),
ProfilePage.routeName: (ctx) => ProfilePage(),
SettingsPage.routeName: (ctx) => SettingsPage(
appLanguage: appLanguage,
),
ChangePassword.routeName: (ctx) => ChangePassword(),
HomeworkScreen.routeName: (ctx) => HomeworkScreen(),
HWDetails.routeName: (ctx) => HWDetails(),
NewsPage.routeName: (ctx) => NewsPage(),
QuestionAndAnswers.routeName: (ctx) => QuestionAndAnswers(),
MyDownloads.routeName: (ctx) => MyDownloads(),
},
),
),
),
),
);
}
}
Here how I tried to implement
class AppLocalization {
final Locale locale;
AppLocalization(this.locale);
static AppLocalization of(BuildContext context) {
return Localizations.of<AppLocalization>(context, AppLocalization);
}
static const LocalizationsDelegate<AppLocalization> delegate =
_AppLocalizationDelegate();
Map<String, String> _localizedStrings;
Future<bool> load() async {
String jsonString = await rootBundle
.loadString('assets/translations/${locale.languageCode}.json');
Map<String, dynamic> jsonMap = json.decode(jsonString);
_localizedStrings = jsonMap.map((key, value) {
return MapEntry(key, value.toString());
});
return true;
}
String translate(String key) {
return _localizedStrings[key];
}
}
class _AppLocalizationDelegate extends LocalizationsDelegate<AppLocalization> {
// This delegate instance will never change (it doesn't even have fields!)
// It can provide a constant constructor.
const _AppLocalizationDelegate();
#override
bool isSupported(Locale locale) {
// Include all of your supported language codes here
return ['ru', 'uz'].contains(locale.languageCode);
}
#override
Future<AppLocalization> load(Locale locale) async {
AppLocalization localizations = new AppLocalization(locale);
await localizations.load();
return localizations;
}
#override
bool shouldReload(_AppLocalizationDelegate old) => false;
}
And my provider :
class AppLanguage extends ChangeNotifier {
Locale _appLocale = Locale('ru');
Locale get appLocal => _appLocale ?? Locale("ru");
fetchLocale() async {
var prefs = await SharedPreferences.getInstance();
if (prefs.getString('language_code') == null) {
_appLocale = Locale('ru');
return Null;
}
_appLocale = Locale(prefs.getString('language_code'));
return Null;
}
void changeLanguage(Locale type) async {
var prefs = await SharedPreferences.getInstance();
if (_appLocale == type) {
return;
}
if (type == Locale("uz")) {
_appLocale = Locale("uz");
await prefs.setString('language_code', 'uz');
await prefs.setString('countryCode', 'UZ');
} else {
_appLocale = Locale("ru");
await prefs.setString('language_code', 'ru');
await prefs.setString('countryCode', 'RU');
}
notifyListeners();
}
}
If this is not a correct way , how can I implement this feature ?
The way you are trying to implement is explained in this article, if you'd like a different approach I'd suggest you to check out Easy Localization from pub.dev which is currently handling well all the Flutter internationalization features.
MY SOLUTION
Note:
Used get_storage for persistent key/value storage.
The above question has been asked in reference to this tutorial
main.dart
void main() async {
...
///Initialize Storage
await GetStorage.init();
...
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
...
///Check for locale
AppLanguage language = AppLanguage();
language.fetchLocale();
final providers = [
/**
* Add all providers in here like below
* ChangeNotifierProvider<ThemeModel>(create: (context) => ThemeModel(),),
*/
...
///Language provider
ChangeNotifierProvider<AppLanguage>(
create: (context) => language,
),
...
];
return MultiProvider(
providers: providers,
child: Consumer<AppLanguage>(
builder: (context, model, _) {
/**
* Access other providers apart from language like below
* Provider.of<ThemeModel>(context).theme,
*/
...
return MaterialApp(
locale: model.appLocal,
supportedLocales: [
Locale('ru', 'RU'),
Locale('uz', 'UZ'),
],
localizationsDelegates: [
AppLocalization.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
...
);
},
),
);
}
}
Here is the provider language.dart
class AppLanguage extends ChangeNotifier {
Locale _appLocale = Locale('ru');
Locale get appLocal => _appLocale;
fetchLocale() async {
var storage = GetStorage();
if (!(storage.hasData('language_code'))) {
_appLocale = Locale('ru');
return Null;
}
_appLocale = Locale(storage.read('language_code'));
return Null;
}
Future<void> updateLanguage(Locale type) async {
var storage = GetStorage();
if (_appLocale == type) {
return;
}
if (type == Locale('uz')) {
_appLocale = Locale('sw');
await storage.write('language_code', 'uz');
await storage.write('countryCode', 'UZ');
} else {
_appLocale = Locale('ru');
await storage.write('language_code', 'ru');
await storage.write('countryCode', 'RU');
}
notifyListeners();
}
}
The AppLocalization class on the above question has been implemented correctly.
USAGE
Widget build(BuildContext context) {
...
final languageModel = Provider.of<LanguageModel>(context);
...
return Scaffold(
body: ListView(
children: [
GestureDetector(
...
onTap: () {
languageModel.appLocal == Locale('ru')
? languageModel.updateLanguage(Locale('uz'))
: languageModel.updateLanguage(Locale('ru'));
},
),
],
),
);
}
Thoughts
Language provider should be consumed first and the rest to follow i.e ApiService.. e.t.c.
Any misconception on my code and bugs, don't hesitate to comment. I will reply as soon as possible.
I am using provider to handle my app themeing.
This is my main.dart file
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import './providers/theme_manager.dart';
//screens
import './screens/home_screen.dart';
import './screens/news_screen.dart';
import './screens/department_screen.dart';
import './screens/contact_us_screen.dart';
import './screens/admission_screen.dart';
import './screens/placements_screen.dart';
import './screens/about_screen.dart';
import './screens/settings_screen.dart';
void main() => runApp(
ChangeNotifierProvider(
create: (_) => ThemeManager(),
child: SfitApp(),
),
);
class SfitApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Consumer<ThemeManager>(builder: (context, themeManager, _) {
return MaterialApp(
title: "SFIT App",
theme: themeManager.themeData,
//routes
initialRoute: HomeScreen.routeName,
routes: {
HomeScreen.routeName: (context) => HomeScreen(),
AboutScreen.routeName: (context) => AboutScreen(),
NewsScreen.routeName: (context) => NewsScreen(),
AdmissionScreen.routeName: (context) => AdmissionScreen(),
DepartmentScreen.routeName: (context) => DepartmentScreen(),
PlacementsScreen.routeName: (context) => PlacementsScreen(),
ContactUsScreen.routeName: (context) => ContactUsScreen(),
//other
SettingsScreen.routeName: (context) => SettingsScreen(),
},
);
});
}
}
What happens if when i open my app for the first time. It builds my app with a light theme and then rebuilds if i have set to dark theme. Thus there is a moment where my app is light. Is there a way to rebuild with the set theme.
This is the code for my provider.
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../constants/app_themes.dart';
class ThemeManager with ChangeNotifier {
ThemeData _themeData;
final _kThemePreference = "";
String _theme = 'Light';
ThemeManager() {
_loadTheme();
}
void _loadTheme() {
SharedPreferences.getInstance().then((preference) {
int preferredTheme = preference.getInt(_kThemePreference) ?? 0;
_themeData = appThemeData[AppTheme.values[preferredTheme]];
if (AppTheme.values[preferredTheme] == AppTheme.Dark) {
_theme = 'Dark';
}
notifyListeners();
});
}
ThemeData get themeData {
if (_themeData == null) {
_themeData = appThemeData[AppTheme.Light];
}
return _themeData;
}
String enumName(AppTheme enumValue) {
return enumValue.toString().split('.')[1];
}
String get theme {
return _theme;
}
set themeValue(String theme) {
_theme = theme;
}
setTheme(AppTheme theme) async {
_themeData = appThemeData[theme];
notifyListeners();
var preference = await SharedPreferences.getInstance();
preference.setInt(_kThemePreference, AppTheme.values.indexOf(theme));
}
}
I found the answer.
If you use shared preferences in void main to load your theme preference then there will be a brief moment where your app will load with the light of default theme and then change to your chosen theme. To prevent this just add WidgetsFlutterBinding.ensureInitialized(); to your void main().
eg.
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
//Initialize Shared Preferences instance
var preference = await SharedPreferences.getInstance();
//Get Theme data
int preferredTheme = preference.getInt("themePreference") ?? 0;
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider<ThemeManager>(
create: (_) => ThemeManager(preferredTheme),
),
],
child: SfitApp(),
),
);
}
I'm having a problem with my app.
The situation is: i have a very simple login system on the app and i save the logged user using SharedPreferences.
But if the user leaves the app and then return it will open the login screen again, so i want to skip the login screen if the user is logged.
So in my main i put a function to check if there is login information, if yes it would redirect right to the app page or to the login page if not.
But when i try to call the app page it always calls the page setted on the Home part.
How can i solve this?
Is there any way to make it ignore the Home?
Is there a way to make the "if" part on the home? Would be the better solution but its not possible.
Also i know i'm not using the best way to make this control, but it works (despite of this problem i have now, sure) and if you have any tips on making it better i would appreciate.
Heres my code:
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
void main() => runApp(new MyApp());
class _MyAppState extends State<MyApp> {
Future<void> verificaLogin() async {
print("running ok"); //just to test if the function runs
final prefs = await SharedPreferences.getInstance();
final key = 'usuario';
final value = prefs.getString(key);
print('saved tester $value');
String usu = value; /
if (usu.isEmpty) {
BuildContext context;
Navigator.push(
context,
MaterialPageRoute(builder: (context) => LoginScreen()), //sends to loginscreen if not logged
);
}
if (usu.isNotEmpty) {
BuildContext context;
Navigator.of(context)
.pushReplacement(MaterialPageRoute(builder: (context) => Pedidos())); //sends to main (not main.dart) app page
}
}
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) => verificaLogin());
}
Widget build(BuildContext context) {
return BotToastInit(
child: MaterialApp(
navigatorObservers: [BotToastNavigatorObserver()],
title: "Test App",
theme: ThemeData(
primarySwatch: Colors.green,
),
debugShowCheckedModeBanner: false,
home: LoginScreen(), //i'm calling the loginscreen, it ignores the function on the top
),
);
}
}
Please make one SplashScreen like below it resolves your issue..
and call this as home: SplashScreen(),
class SplashScreen extends StatefulWidget {
#override
_SplashScreenState createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
String loginData = "";
SharedPreferences sharedPreferences;
void initState() {
super.initState();
readFromStorage();
}
void readFromStorage() async {
sharedPreferences = await SharedPreferences.getInstance();
final key = 'usuario';
loginData = sharedPreferences.getString(key);
if (loginData.isNotEmpty) {
Future.delayed(const Duration(milliseconds: 3000), () {
setState(() {
Navigator.of(context)
.pushReplacement(MaterialPageRoute(builder: (context) => Pedidos()));
});
});
} else {
Future.delayed(const Duration(milliseconds: 3000), () {
setState(() {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => LoginScreen()),);
});
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FlutterLogo(
size: 100.0,
),
],
)),
);
}
}
Hi you can use a FutureBuilder at vefiricaLoigin, and then Material App at home use verificaLogin,
https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html
verificaLogin() {
return FutureBuilder<Widget>(
future: SharedPreferences.getInstance(),
builder: (BuildContext context, prefs) {
final key = 'usuario';
final value = prefs.getString(key);
String usu = value;
if (usu.isEmpty) {
return LoginScreen()l
} else {
return Pedidos();
}
);
}
Widget build(BuildContext context) {
return MaterialApp(
title: "Test App",
theme: ThemeData(
primarySwatch: Colors.green,
),
debugShowCheckedModeBanner: false,
home: verificaLogin()
);
}