I'm using GetX for state management in a Flutter web application. I have created an authcontroller with a login method. When I call the method anywhere in the application it works and the UI changes with the new state. But when I refresh explorer the controller is reset and login state is lost.
I have created a small version of the code easy to reproduce:
import 'package:flutter/material.dart';
import 'package:get/get.dart';
void main() {
runApp( MyApp());
}
class MyApp extends StatelessWidget {
AuthController authController = Get.put(AuthController(), permanent: true);
MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return GetMaterialApp(
title: 'Flutter Demo',
smartManagement: SmartManagement.keepFactory,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomeScreen(),
);
}
}
class HomeScreen extends StatelessWidget {
HomeScreen({Key? key}) : super(key: key);
final AuthController authController = Get.find();
#override
Widget build(BuildContext context) {
return Center(
child: ElevatedButton(
child: Obx(() => authController.isAuthenticated.value ? Text("authenticated") : Text("authenticate")),
onPressed: () {
authController.login();
},
)
);
}
}
class AuthController extends GetxController {
var isAuthenticated = false.obs;
void login() {
isAuthenticated.value = true;
update();
}
}
As you can see I'm using the permanent:true prop but the controller still is re-initialized.
This is how the issue looks:
Is there any prop or config that I'm missing? how to avoid this behavior?
The error in your code is that the Controller is recreated every time the page is refreshed. That is why Use GetStorage package.
import 'package:flutter/material.dart';
import 'package:get/get.dart';
void main() async {
await GetStorage.init(); // Add this first
runApp( MyApp());
}
class MyApp extends StatelessWidget {
AuthController authController = Get.put(AuthController(), permanent: true);
MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return GetMaterialApp(
title: 'Flutter Demo',
smartManagement: SmartManagement.keepFactory,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomeScreen(),
);
}
}
class HomeScreen extends StatelessWidget {
HomeScreen({Key? key}) : super(key: key);
final AuthController authController = Get.find();
#override
Widget build(BuildContext context) {
return Center(
child: ElevatedButton(
child: _isLoged.read('login') == false ||
_isLoged.read('login') == null ? Text("authenticate")
: Text("authenticated") ,
onPressed: () {
authController.login();
},
)
);
}
}
class AuthController extends GetxController {
var _isLoged = GetStorage(); // In the controller, it is necessary to save the entry with this
void login() async {
await _isLoged.write('login', true);
update();
}
}
Related
I have made a new file in my views folder but whenever I turn on the emulator and run the code, it just says "Hello World".
Is there a way I can set the starting point of the project to be on this new file? Because it only seems to turn on the main.dart file.
This is the code that is in the views file called home_page.dart . It is supposed to just say "Hi" 10 times.
import 'package:flutter/material.dart';
import '../models/post.dart';
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
List<Post>? posts;
var isLoaded = false;
#override
void initState() {
super.initState();
//fetch data from API
getData();
}
getData() async {
// posts = await
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Posts'),
),
body: ListView.builder(
itemCount: 10,
itemBuilder: (context, index) {
return Container(
child: Text('Hi'),
);
},
)
);
}
}
in flutter the main.dart file is the first file
import 'package:flutter/material.dart';
import 'package:get/get_navigation/src/root/get_material_app.dart';
void main() {
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 MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),//add your home page here
);
}
}
I'm using desktop_webview_window: ^0.1.6 flutter package.
import 'package:flutter/material.dart';
import 'package:desktop_webview_window/desktop_webview_window.dart';
import 'package:bitsdojo_window/bitsdojo_window.dart';
how can i only display the webview window without the white screen in flutter desktop?
here is full the code:
import 'package:flutter/material.dart';
import 'package:desktop_webview_window/desktop_webview_window.dart';
import 'package:bitsdojo_window/bitsdojo_window.dart';
void main(List<String> args) async {
debugPrint('args: $args');
WidgetsFlutterBinding.ensureInitialized();
if (runWebViewTitleBarWidget(args)) {
return;
}
runApp(const MyApp());
doWhenWindowReady(
() {
final initialSize = Size(800, 650);
final minSize = Size(600, 450);
final maxSize = Size(1200, 850);
appWindow.maxSize = maxSize;
appWindow.minSize = minSize;
appWindow.size = initialSize; //default size
appWindow.show();
},
);
}
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 MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Vroom',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const WebViewExample(),
);
}
}
class WebViewExample extends StatefulWidget {
const WebViewExample({Key? key}) : super(key: key);
#override
_WebViewExampleState createState() => _WebViewExampleState();
}
class _WebViewExampleState extends State<WebViewExample> {
#override
void initState() {
super.initState();
web();
}
void web() async {
final webview = await WebviewWindow.create(
configuration: const CreateConfiguration(title: "google"),
);
webview.launch("https://google.com/");
}
#override
Widget build(BuildContext context) {
return Scaffold();
}
}
the problem is that i want to execute this part only:
void web() async {
final webview = await WebviewWindow.create(
configuration: const CreateConfiguration(title: "google"),
);
webview.launch("https://google.com/");
}
any help please ?
If flutter app starts with:
void main() {
WidgetsFlutterBinding.ensureInitialized();
Then clock.now() prints not mocked value if outside of build method.
Since I am using withClock in my tests, it makes my app unable to be properly tested as sometimes clock.now() returns mocked value, and sometimes real current time.
Please find the following example, and note that _printClockNow prints DateTime.now(), but it should print year 1990.
If you removed line WidgetsFlutterBinding.ensureInitialized() then it works as expected.
Minimum reproduceable code:
import 'package:clock/clock.dart';
import 'package:flutter/material.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
withClock(Clock.fixed(DateTime(1990)), () {
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 MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
void _printClockNow() {
print('_printClockNow is: ${clock.now()}'); // prints real NOW
setState(() {});
}
#override
Widget build(BuildContext context) {
print('build clock.now() is: ${clock.now()}'); // prints mocked NOW
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[Dummy()],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _printClockNow,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
class Dummy extends StatelessWidget {
#override
Widget build(Object context) {
return Text('t: ${clock.now()}');
}
}
Try with putting WidgetsFlutterBinding inside withClock.
void main() {
withClock(Clock.fixed(DateTime(1990)), () {
WidgetsFlutterBinding.ensureInitialized();
runApp(const MyApp());
});
}
I'm trying to take value from the method channel and using the value I'm trying to navigate another screen. When I try to navigate from TextButton onclick it's navigating but when I try to navigate from the value received by the method channel it's not navigating to another screen.
Example: I'm receiving openScreen1 from the method channel in the below code from methodCall.method and assigning the method to route variable but the page is not navigating
main_screen.dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:gg_app/screen1.dart';
import 'package:gg_app/screen2.dart';
class HomeScreen extends StatefulWidget {
static const routeName = "Home-Screen";
const HomeScreen({Key? key}) : super(key: key);
#override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
static const channel = MethodChannel('scribeemr.in/mic');
#override
void initState() {
// TODO: implement initState
channel.setMethodCallHandler(nativeMethodCallHandler);
super.initState();
}
Future<dynamic> nativeMethodCallHandler(MethodCall methodCall) async {
var route = methodCall.method;
await navigateTo(route, context);
}
Future<dynamic> navigateTo(String route, BuildContext context) async {
switch (route) {
case "openScreen1":
await Navigator.of(context).pushNamed(Screen1.routeName);
break;
case "openScreen2":
await Navigator.of(context).pushNamed(Screen2.routeName);
break;
default:
break;
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Home Screen")),
body: Column(
children: [
TextButton(
onPressed: () {
navigateTo("openScreen1", context);
},
child: Text("Screen 1")),
TextButton(
onPressed: () {
navigateTo("openScreen2", context);
},
child: Text("Screen 2")),
],
),
);
}
}
main.dart
import 'package:flutter/material.dart';
import 'package:gg_app/home_screen.dart';
import 'package:gg_app/screen1.dart';
import 'package:gg_app/screen2.dart';
void main() {
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 MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomeScreen(),
routes: {
HomeScreen.routeName: (context) => HomeScreen(),
Screen1.routeName: (context) => Screen1(),
Screen2.routeName: (context) => Screen2(),
},
);
}
}
screen1.dart
import 'package:flutter/material.dart';
class Screen1 extends StatefulWidget {
static const routeName = "Screen1";
const Screen1({ Key? key }) : super(key: key);
#override
State<Screen1> createState() => _Screen1State();
}
class _Screen1State extends State<Screen1> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Screen 1")),
);
}
}
I'm moving to Riverpod and starting with what should be a very simple boolean state management to switch the app theme. I've read many "how to" to do this exact thing, but none focus on the basics itself, are using outdated API, use different notifiers and providers, and add more features than needed. So now I'm lost.
versions:
shared_preferences: ^2.0.13
flutter_riverpod: ^1.0.3
Simple story:
A model class that extends StateNotifier and has accessor and mutators in it for 1 single boolean value
Very basic app + stateful widget
Problem: I can see, via prints, the state is being altered, but the UI is not changing to when the state is changed. Am I not using StateNotifier and StateProvider properly for a simple boolean state?
Thank you for any insight and guidance! Code follows:
===
model class with accessors / mutators
class RiverThemeDarkModel extends StateNotifier<bool> {
RiverThemeDarkModel() : super(false) {}
bool get isDark {
print("model asked for dark status: ${state}");
return state;
}
toggleDark() {
print("toggler has listeners: ${hasListeners}");
state = !state;
print("toggled to: ${state}");
}
set isDark(bool value) {
state = value;
}
}
Very basic app + stateful widget
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {
runApp(ProviderScope(child: MyApp()));
}
final themeProvider = StateProvider((ref) => RiverThemeDarkModel());
class MyApp extends ConsumerWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context, WidgetRef ref) {
final darkValueModel = ref.watch(themeProvider);
print("consumer widget: dark value is ${darkValueModel.state} and listeners: ${darkValueModel.hasListeners}");
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: darkValueModel.isDark? ThemeData.dark() : ThemeData.light(),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends ConsumerStatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
ConsumerState<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends ConsumerState<MyHomePage> {
#override
Widget build(BuildContext context) {
final darkValueModel = ref.watch(themeProvider);
print("build of state");
return Scaffold(
appBar: AppBar(
title: Text(darkValueModel.isDark ? "Dark Mode" : "Light Mode"),
actions: [
IconButton(
icon: Icon(darkValueModel.isDark
? Icons.nightlight_round
: Icons.wb_sunny),
onPressed: () {
ref.read(themeProvider).toggleDark();
print("Got pressed button, after setting is ${ref.read(themeProvider).isDark} \n\n");
})
],
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'Something',
),
],
),
),
);
}
}
From your code snippet, what you did here
final themeProvider = StateProvider((ref) => RiverThemeDarkModel());
was to provide to your UI access to the default state of your class which is false. thus if you change it to true and rerun the app it changes the theme. now what you want to do is expose access to the stateNotifier of your class which listens to the state changes of the class and notifies its listeners. To achieve this you need to use a StateNotifierProvider this way...
final themeProvider = StateNotifierProvider<RiverThemeDarkModel, bool>(
(ref) => RiverThemeDarkModel());
Now the rest of the code to help your implementation is as follows
For your Model.
class RiverThemeDarkModel extends StateNotifier<bool> {
RiverThemeDarkModel() : super(false) {}
//u don't need this 'getter' piece of code
bool get isDark {
print("model asked for dark status: ${state}");
return state;
}
toggleDark() {
print("toggler has listeners: ${hasListeners}");
state = !state;
print("toggled to: ${state}");
}
//neither do you need this
set isDark(bool value) {
state = value;
}
}
For your UI...
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {
runApp(ProviderScope(child: MyApp()));
}
final themeProvider = StateNotifierProvider<RiverThemeDarkModel, bool>(
(ref) => RiverThemeDarkModel());
class MyApp extends ConsumerWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context, WidgetRef ref) {
final darkValueModel = ref.watch(themeProvider);
print("consumer widget: dark value is ${darkValueModel.state} and listeners: ${darkValueModel.hasListeners}");
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: darkValueModel? ThemeData.dark() : ThemeData.light(),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends ConsumerStatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
ConsumerState<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends ConsumerState<MyHomePage> {
#override
Widget build(BuildContext context) {
final darkValueModel = ref.watch(themeProvider);
print("build of state");
return Scaffold(
appBar: AppBar(
title: Text(darkValueModel ? "Dark Mode" : "Light Mode"),
actions: [
IconButton(
icon: Icon(darkValueModel
? Icons.nightlight_round
: Icons.wb_sunny),
onPressed: () {
ref.read(themeProvider.notifier).toggleDark();
print("Got pressed button, after setting is ${ref.read(themeProvider)} \n\n");
})
],
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'Something',
),
],
),
),
);
}
}
Check out the documentation too. It clarifies some things you won't get from most tutorials. https://riverpod.dev/docs/concepts/reading
use this
final themeProvider = StateNotifierProvider<RiverThemeDarkModel, bool>(
(ref) => RiverThemeDarkModel());
Instead of this
final themeProvider = StateProvider(
(ref) => RiverThemeDarkModel());
Small Change on your code
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {
runApp(ProviderScope(child: MyApp()));
}
final themeProvider = StateNotifierProvider<RiverThemeDarkModel, bool>(
(ref) => RiverThemeDarkModel());
class MyApp extends ConsumerWidget {
MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context, WidgetRef ref) {
final darkValueModel = ref.watch(themeProvider);
print(
"consumer widget: dark value is ${ref.read(themeProvider.notifier).state} and listeners: ${ref.read(themeProvider.notifier).hasListeners}");
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme:darkValueModel
? ThemeData.dark()
: ThemeData.light(),
home: MyHomePage(title: 'Flutter Demo Home Page${ref.read(themeProvider.notifier).isDark}'),
);
}
}
class MyHomePage extends ConsumerStatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
ConsumerState<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends ConsumerState<MyHomePage> {
#override
Widget build(BuildContext context) {
final darkValueModel = ref.watch(themeProvider);
print("build of state");
return Scaffold(
appBar: AppBar(
title: Text(ref.read(themeProvider.notifier).isDark
? "Dark Mode${widget.title}"
: "Light Mode"),
actions: [
IconButton(
icon: Icon(ref.read(themeProvider.notifier).isDark
? Icons.nightlight_round
: Icons.wb_sunny),
onPressed: () {
ref.read(themeProvider.notifier).toggleDark();
print(
"Got pressed button, after setting is ${ref.read(themeProvider.notifier).isDark} \n\n");
})
],
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"Something ${ref.read(themeProvider.notifier).isDark} ",
),
],
),
),
);
}
}
class RiverThemeDarkModel extends StateNotifier<bool> {
RiverThemeDarkModel() : super(false) {}
bool get isDark {
print("model asked for dark status: ${state}");
return state;
}
toggleDark() {
print("toggler has listeners: ${hasListeners}");
state = !state;
print("toggled to: ${state}");
}
set isDark(bool value) {
state = value;
}
}