Instruction page that only loads after installation - flutter

I have a page in my app that has some icons and animations on how to use the app.
I want to load this page on the first launch after installation and then I want any other launch of the app to go straight to the home page.
How can this be done?
I have seen a couple threads that confuse this question with splash screens, I only want this page to be launched once after installation and then never again.
Thank you

You must create splash screen and in this page check the shared preference that tell you if you already showed intro page or not
if you showed that page you can navigate to main page otherwise navigate to intro page
in intro page show whatever you want to show and in when introduction is over set the isIntroShowed or to true on shared preference
like below code
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: SplashScreen(),
);
}
}
class SplashScreen extends StatefulWidget {
SplashScreen({Key key}) : super(key: key);
#override
_SplashScreenState createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
#override
void initState() {
SharedPreferences.getInstance().then((prefs){
var isShowed =prefs.getBool("isIntroShowed");
if(isShowed!=null && isShowed)
{
//navigate to main page
}
else{
//navigate to intro page
}
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(child: CircularProgressIndicator(),),
);
}
}
class IntroPage extends StatelessWidget {
const IntroPage({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child:FlatButton(child: Text('intro done'),onPressed: ()async{
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setBool('isIntroShowed', true);
// navigate to main content
},)
),
);
}
}

Related

How do I run a different part of a folder in flutter (VS Codium)

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
);
}
}

Back button is not working properly with webview flutter

I am building a flutter app which simply views website URL. but I press back instead of navigating back it exists the app. I searched for solutions but no solution helped me. or that code doesn't support with latest flutter...
Here is my code
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.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'),
);
}
}
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> {
#override
Widget build(BuildContext context) {
return WebView(
initialUrl: 'https://flutter.dev',
);
}
}
This is happening because you are not coming from any page.
This is how currently your structure is following...
main() ===> MyApp() ===> MyHomePage(title: 'Flutter Demo Home Page') ===> "your-web-vide-page".
so if you would like to work back button then there has to be any page from the page you are going to like...
Navigator.push(context, MaterialPageRoute(builder: (context) {
return //<=== your-page-here
},));
You need to handle this manually.
Create a webView Controller first:
final Completer<WebViewController _controller =
Completer<WebViewController ();
and then create actions in the appBar, and add buttons to handle back and forward actions.
so on back button you would call:
controller.goBack();
and forward:
controller.goForward();
to handle the back button on android device you need to wrap your scaffold with willpopscop and then handle the case inside onWillPop function.

Flutter GetX Refreshing Explorer restart controllers

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();
}
}

How to pass device width to other screen inside initState in Flutter?

I want to pass deviceWidth and deviceHeight to other screen. Here is my code:
class _SplashPageState extends State<SplashPage> {
#override
void initState(){
super.initState();
var deviceHeight = MediaQuery.of(context).size.height;
var deviceWidth = MediaQuery.of(context).size.width;
Future.delayed(Duration(seconds: 1), () async {
await Navigator.pushNamed(context, "/home");
});
}
But when I run this code, I am getting this error:
dependOnInheritedWidgetOfExactType<MediaQuery>() or dependOnInheritedElement() was called before _SplashPageState.initState() completed.
How to fix this? And the way I'm doing is right way?
The issue is that accessing anything from the context from init state might not guarantee that the widget is already built. The solution is to trigger a method after the first initial render of the frame. That way, the context is properly updated with the current context of that the widget has.
Try this example code i wrote:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
void initState() {
super.initState();
WidgetsBinding.instance!.addPostFrameCallback((_) {
var deviceHeight = MediaQuery.of(context).size.height;
var deviceWidth = MediaQuery.of(context).size.width;
print(deviceHeight);
print(deviceWidth);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
);
}
}
This would print the width and height of the screen. Without any errors. This is because i am using
WidgetsBinding.instance!.addPostFrameCallback((_) {
});
Essentially what it does is the method will be executed after the first frame is built. more info here https://api.flutter.dev/flutter/scheduler/SchedulerBinding/addPostFrameCallback.html
I would like to add that it's a weird use case to get the screen width and height from within init state. There might be a better approach to what you are trying to achieve.

Flutter : SharedPreferences not fetching value at app start

I am trying to store a value and based on the value I want to navigate to LandinPage or HomePage. However when my app loads I am not able to get the SharedPreferences value. Currently, the value is set on click of a button in Landing page, and when I close/minimize the app. I don't even get to see the print messages from main.dart and can't fetch values. What am I doing wrong?
Here is my code:
import 'package:credit/src/pages/landing.dart';
import 'package:flutter/material.dart';
import 'package:credit/src/pages/credit/home.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
MyApp({Key key}) : super(key: key);
_LoadingPageState createState() => _LoadingPageState();
}
class _LoadingPageState extends State<MyApp> {
#override
void initState() {
super.initState();
getUserStatus().then((userStatus) {
if (userStatus == null) {
Navigator.of(context)
.push(MaterialPageRoute<Null>(builder: (BuildContext context) {
return LandingPage();
}));
} else {
Navigator.of(context)
.push(MaterialPageRoute<Null>(builder: (BuildContext context) {
return HomePage();
}));
}
});
}
#override
Widget build(BuildContext context) {
return Container(
child: Center(
child: CircularProgressIndicator(),
));
}
}
Future<String> getUserStatus() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String userStatus = prefs.getString('userstatus');
print("==On Load Check ==");
print(userStatus);
return userStatus;
}
You may need to use a "loading page" that is first loaded before any of your two pages:
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'An App',
home: LoadingPage(),
routes: {
'/landing': (context) => LandingPage(),
'/home': (context) => HomePage(),
}
);
}
}
class LoadingPage extends StatefulWidget {
LoadingPage({Key key}) : super(key: key);
_LoadingPageState createState() => _LoadingPageState();
}
class _LoadingPageState extends State<LoadingPage> {
#override
void initState() {
super.initState();
loadPage();
}
loadPage() {
getUserStatus().then((userStatus) {
if (userStatus == null) {
Navigator.of(context).pushNamed('/landing');
} else {
Navigator.of(context).pushNamed('/home');
}
});
}
#override
Widget build(BuildContext context) {
return Container(
child: Center(
child: CircularProgressIndicator(),
));
}
}
class HomePage extends StatefulWidget {
HomePage({Key key}) : super(key: key);
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return Container(
child: Text('Home Page'),
);
}
}
class LandingPage extends StatefulWidget {
LandingPage({Key key}) : super(key: key);
_LandingPageState createState() => _LandingPageState();
}
class _LandingPageState extends State<LandingPage> {
#override
void initState() {
super.initState();
setUserStatus('done');
}
#override
Widget build(BuildContext context) {
return Container(
child: Text('Landing'),
);
}
}
Future<String> getUserStatus() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String userStatus = prefs.getString('userStatus');
print("==On Load Check ==");
print(userStatus);
return userStatus;
}
Future<bool> setUserStatus(String userStatus) async{
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('userStatus', userStatus);
return true;
}
You've declared a method main of MyApp but it never gets called. The main that starts the app is the one with runApp in it. You could move the prefs.getString() into the real main (having made it async) and then pass the value into the MyApp widget as a parameter.
I feel like Willie's answer may be just as good, but here's another approach.
Overall, my approach would be to load the main home page automatically, and then in the initstate of the home page, check to see if this is the user's first visit to the app. If so, pop the landing page on top immediately. I've used this approach successfully without the user having a poor experience.
Below is the default app but with your SharedPreferences code moved to the appropriate spot.
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
var userStatus;
//If user status is null, then show landing page.
Future<void> checkUserStatus() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
userStatus = prefs.getString('userstatus');
print("==On Load Check ==");
print(userStatus);
if (userStatus == null) {
Navigator.push(context, MaterialPageRoute(builder: (context) => LandingPage()));
}
}
#override
void initState() {
super.initState();
//Call check for landing page in init state of your home page widget
checkUserStatus();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
class LandingPage extends StatefulWidget {
#override
_LandingPageState createState() => _LandingPageState();
}
class _LandingPageState extends State<LandingPage> {
#override
Widget build(BuildContext context) {
//Build landing page here.
return Container();
}
}
I know this question is old and already been answered but for my situation, Richard Heap's answer was more suitable so I would like to add a code snippet for others.
I only cite part of it, so please modify it if you are going to use it for your app. After the Landing/Welcome page is viewed by user, update the preference by setBool and it won't show up after that.
void main() async {
// do whatever
SharedPreferences prefs = await SharedPreferences.getInstance();
bool hideWelcome = prefs.getBool('hideWelcome') ?? false;
// start your app
runApp(MyApp(hideWelcome));
}
class MyApp extends StatelessWidget {
final hideWelcome;
MyApp(this.hideWelcome);
#override
Widget build(BuildContext context) {
return MaterialApp(
// other setting like theme, title
initialRoute: hideWelcome ? '/' : '/welcome',
routes: {
'/': (context) => MyHomePage(),
'/welcome': (context) => WelcomePage(),
// other pages
}
);
}
you must add
#override
void initState() {
getUserStatus();
super.initState();
}
var name;
void getUserStatus() async {
SharedPreferences prefs= await SharedPreferences.getInstance();
setState(() {
userStatus = prefs.getString("userStatus");
});
}