Am new to flutter. I am creating a flutter app involving google mobile ads. I want to implement a banner AD on the bottom navigation bar of every screen. Is there a way to implement the banner ad once on one screen (on the bottom nav bar) and persist it throughout all screens than creating instances of a banner Ad on each screen and creating a bottom navigation bar? I Am using getx for routes and here's one of my screens where I implemented the banner ad.
class ProgrammeScreen extends StatefulWidget {
const ProgrammeScreen({Key? key}) : super(key: key);
static const routeName = '/ProgrammeScreen';
#override
State<ProgrammeScreen> createState() => _ProgrammeScreenState();
}
class _ProgrammeScreenState extends State<ProgrammeScreen> {
late BannerAd _bottomBannerAd1;
bool _isBottomBannerLoaded = false;
void createBottomBannerAd() {
_bottomBannerAd1 = BannerAd(
listener: BannerAdListener(onAdLoaded: (_) {
setState(() {
_isBottomBannerLoaded = true;
});
}, onAdFailedToLoad: (ad, error) {
ad.dispose(); }),
adUnitId: bannerKey,
size: AdSize.banner,
request: const AdRequest());
_bottomBannerAd1.load();
}
#override
void initState() {
createBottomBannerAd();
super.initState();
}
#override
void dispose() {
_bottomBannerAd1.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Programme'),
backgroundColor: appBarColor(context),
),
bottomNavigationBar: _isBottomBannerLoaded ? SizedBox(
height: _bottomBannerAd1.size.height.toDouble(),
width: _bottomBannerAd1.size.width.toDouble(),
child: AdWidget(ad: _bottomBannerAd1),
) : const SizedBox(),
body: SizedBox(
width: UIParameters.getWidth(context),
height: UIParameters.getHeight(context),
child: Padding(
padding: const EdgeInsets.all(kMobileScreenPadding),
child: Column(
children: [
Expanded(
child: StreamBuilder<QuerySnapshot>(
stream: estudieeFR.snapshots(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return const Text('Something went wrong');
} else if (snapshot.hasData) {
return ListView.separated(
shrinkWrap: true,
itemCount: snapshot.data!.docs.length,
itemBuilder: (BuildContext context, int index) {
final data = snapshot.data!.docs[index];
return ContentCard(
title: data['name'],
icon: Icons.arrow_forward_ios,
onPressed: () => Get.to(
() => YearScreen(
programId: snapshot.data!.docs[index].id),
),
);
},
separatorBuilder: (BuildContext context, int index) {
return const Divider();
},
);
} else {
return Indicators.circularIndicator;
}
}),
)
],
),
),
),
);
}
}
Heres my main.dart file with the root widget(MyApp)
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static final GlobalKey<NavigatorState> navigatorKey = GlobalKey();
#override
Widget build(BuildContext context) {
return GetMaterialApp(
navigatorKey: navigatorKey,
defaultTransition: Transition.rightToLeftWithFade,
initialRoute: '/',
getPages: AppRoutes.pages(),
debugShowCheckedModeBanner: false,
);
}
}
I don't want to copy the same code in all the stateful widgets but rather implement a single bottom navigation banner Ad that will persist all screens. Is there any way to achieve this?
If you do not want to write the same code in all screens, then you could make modifications in the builder function of MaterialApp to include your banner ad there as:
MaterialApp(
builder: (BuildContext context, Widget? child) {
return Column(
children: [
Expanded(child: child!),
// your banner widget here,
],
);
},
home: HomeScreen(),
);
Create another dart file and add this code
import 'package:google_mobile_ads/google_mobile_ads.dart';
late BannerAd bannerAd;
loadBanner() {
bannerAd = BannerAd(
adUnitId: BannerAd.testAdUnitId,
size: AdSize.fullBanner,
request: const AdRequest(),
listener: BannerAdListener(
// Called when an ad is successfully received.
onAdLoaded: (Ad ad) => print('Ad loaded.'),
// Called when an ad request failed.
onAdFailedToLoad: (Ad ad, LoadAdError error) {
// Dispose the ad here to free resources.
ad.dispose();
print('Ad failed to load: $error');
},
// Called when an ad opens an overlay that covers the screen.
onAdOpened: (Ad ad) => print('Ad opened.'),
// Called when an ad removes an overlay that covers the screen.
onAdClosed: (Ad ad) => print('Ad closed.'),
// Called when an impression occurs on the ad.
onAdImpression: (Ad ad) => print('Ad impression.'),
),
)..load();
}
Call the loadBanner once and this bannerAd is available in all classes since it's defined in the root
Related
I'm trying to make an example with GetX where I have the following structure:
A HomePage with a list of Widget1, each Widget1 has a list of Widget2.
I'm having two problems:
The first one is that I can only add one Widget1, when I go to add the second one, it shows the following error:
The following assertion was thrown building Widget1(dirty):
setState() or markNeedsBuild() called during build.
This GetX widget cannot be marked as needing to build because the framework is already in the process of building widgets. A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase.
The widget on which setState() or markNeedsBuild() was called was: GetX
The second problem is that when I press the button to add new Widget2, inside Widget1, nothing happens. I put a print in the list of Widget2, and it is being filled, but visually it doesn't show.
I/flutter (27561): list: 1
I/flutter (27561): list: 2
I/flutter (27561): list: 3
I/flutter (27561): list: 4
HomePage:
class HomePage extends GetView<HomeController> {
const HomePage({super.key});
#override
Widget build(BuildContext context) {
final homeController = Get.put(HomeController());
final widget1Controller = Get.put(Widget1Controller());
return Scaffold(
appBar: AppBar(),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () {
final mykey = GlobalKey();
controller.addWidget1(Widget1(mykey: mykey));
},
),
body: GetX<HomeController>(
builder: (_) {
return ListView.builder(
itemBuilder: (context, index) {
return controller.getWidget1[index];
},
itemCount: controller.getWidget1.length,
);
},
),
);
}
}
HomePage Controller:
class HomeController extends GetxController {
final RxList<Widget1> _widget1List = <Widget1>[].obs;
void addWidget1(Widget1 newWidget1) {
_widget1List.add(newWidget1);
}
List<Widget1> get getWidget1 {
return _widget1List;
}
}
Widget1:
class Widget1 extends GetView<Widget1Controller> {
final Key mykey;
const Widget1({required this.mykey, super.key});
#override
Widget build(BuildContext context) {
controller.setListWidget2(mykey);
return GetX<Widget1Controller>(
builder: (_) {
return ExpansionTile(
title: Container(
color: Colors.blue,
width: 50.0,
height: 50.0,
),
children: [
ListView.builder(
itemBuilder: (context, index) {
return controller.getListWidget2(mykey)[index];
},
itemCount: controller.getListWidget2(mykey).length,
shrinkWrap: true,
),
IconButton(
onPressed: () {
controller.addWidget2(const Widget2(), mykey);
debugPrint("list: ${controller.getListWidget2(mykey).length}");
},
icon: const Icon(Icons.add),
)
],
);
},
);
}
}
Widget1 Controller:
class Widget1Controller extends GetxController {
final RxMap<Key, List<Widget2>> _mapListWidget2 = <Key, List<Widget2>>{}.obs;
void setListWidget2(Key mykey) {
_mapListWidget2[mykey] = <Widget2>[];
}
void addWidget2(Widget2 newWidget2, Key mykey) {
_mapListWidget2[mykey]!.add(newWidget2);
}
List<Widget2> getListWidget2(Key key) {
if (_mapListWidget2[key] != null) {
return _mapListWidget2[key]!;
} else {
return <Widget2>[];
}
}
}
Widget2:
class Widget2 extends StatelessWidget {
const Widget2({super.key});
#override
Widget build(BuildContext context) {
return Container(
height: 300.0,
width: 300.0,
color: Colors.red,
);
}
}
Main:
void main() {
runApp(
const GetMaterialApp(
home: HomePage(),
),
);
}
I am really lost, how can i solve these two problems?
You were facing these issues because some of you have not implemented some of the functionality properly as GetX suggests.
Some of the issues were like putting the controller from initial bindings instead of directly putting it inside the build method.
Here's the working code.
Main app and HomePage
void main() {
runApp(
GetMaterialApp(
initialBinding: InitalBinding(),
home: const HomePage(),
),
);
}
class HomePage extends GetView<HomeController> {
const HomePage({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () {
var mykey = UniqueKey();
controller.addWidget1(Widget1(mykey: mykey));
},
),
body: GetX<HomeController>(
builder: (logic) {
return ListView.builder(
itemBuilder: (context, index) {
return logic.getWidget1[index];
},
itemCount: logic.getWidget1.length,
);
},
),
);
}
}
HomeController (It remains the same)
class HomeController extends GetxController {
final RxList<Widget1> _widget1List = <Widget1>[].obs;
void addWidget1(Widget1 newWidget1) {
_widget1List.add(newWidget1);
}
List<Widget1> get getWidget1 {
return _widget1List;
}
}
Widget - 1
class Widget1 extends GetView<Widget1Controller> {
final Key mykey;
const Widget1({required this.mykey, super.key});
#override
Widget build(BuildContext context) {
return GetBuilder<Widget1Controller>(
builder: (controller) {
return ExpansionTile(
title: Container(
color: Colors.blue,
width: 50.0,
height: 50.0,
),
children: [
ListView.builder(
itemBuilder: (context, index) {
return controller.getListWidget2(mykey)[index];
},
itemCount: controller.getListWidget2(mykey).length,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
),
IconButton(
onPressed: () {
if (controller.mapListWidget2.containsKey(mykey)) {
controller.addWidget2(const Widget2(), mykey);
controller.update();
} else {
controller.setListWidget2(mykey);
controller.addWidget2(const Widget2(), mykey);
controller.update();
}
debugPrint("list: ${controller.getListWidget2(mykey).length}");
},
icon: const Icon(Icons.add),
)
],
);
},
);
}
}
Widget - 1 Controller
class Widget1Controller extends GetxController {
final RxMap<Key, List<Widget2>> mapListWidget2 = <Key, List<Widget2>>{}.obs;
void setListWidget2(Key mykey) {
mapListWidget2[mykey] = <Widget2>[];
}
void addWidget2(Widget2 newWidget2, Key mykey) {
mapListWidget2[mykey]!.add(newWidget2);
}
List<Widget2> getListWidget2(Key key) {
if (mapListWidget2[key] != null) {
return mapListWidget2[key]!;
} else {
return <Widget2>[];
}
}
}
Widget - 2 remains the same.
I have put dependency injection in another file called bindings.
Initial Bindings
class InitalBinding implements Bindings {
#override
void dependencies() {
Get.put<HomeController>(HomeController(), permanent: true);
Get.put<Widget1Controller>(Widget1Controller(), permanent: true);
}
}
I am trying to create a view where the user can select the agenda view he wants (day, week, month...).
In my appBar, I have created an action icon where the user can select the agenda view he wants.
When I change the view, the set state does not refresh the agenda view. I do not find what I am missing.
If you could help, it will be appreciated. Thank you.
import 'package:syncfusion_flutter_calendar/calendar.dart';
import 'package:provider/provider.dart';
class CalendarWidget extends StatefulWidget {
CalendarView viewCalendar;
CalendarController _controller = CalendarController();
CalendarWidget(this.viewCalendar,this._controller, {Key? key}) : super(key: key);
#override
State<CalendarWidget> createState() => _CalendarWidgetState(viewCalendar,_controller);
}
class _CalendarWidgetState extends State<CalendarWidget> {
CalendarView viewCalendar;
CalendarController _controller;
#override
_CalendarWidgetState(this.viewCalendar,this._controller);
#override
Widget build(BuildContext context) {
return Column(
children: [
myCalendar(context,viewCalendar,_controller),
const SizedBox(height: 8.0),
],
);
}
}
Widget myCalendar (BuildContext context, view,_controler ) {
final events = Provider.of<EventProvider>(context).events;
final CalendarController _calendarControler = CalendarController();
_calendarControler.view = view;
return SfCalendar(
view: CalendarView.month,
// timeSlotViewSettings:
// const TimeSlotViewSettings(allDayPanelColor: Colors.green),
controller: _controler,
_controler.view = view,
//_controller.view = CalendarView.week,
showNavigationArrow: true,
showWeekNumber: true,
showDatePickerButton: true,
showCurrentTimeIndicator: true,
initialSelectedDate: DateTime.now(),
firstDayOfWeek: 1,
dataSource: EventDataSource(events),
onSelectionChanged: (details) {
final provider = Provider.of<EventProvider>(context, listen: false);
provider.setDate(details.date!);
},
onTap: (details) {
final provider = Provider.of<EventProvider>(context, listen: false);
if (provider.selectedDate == details.date) {
showModalBottomSheet(
context: context,
builder: (context) => const TasksWidget(),
);
}
},
onLongPress: (details) {
final provider = Provider.of<EventProvider>(context, listen: false);
provider.setDate(details.date!);
showModalBottomSheet(
context: context,
builder: (context) => const TasksWidget(),
);
},
);
}
class AgendaOrganize extends StatefulWidget {
const AgendaOrganize ({Key? key}) : super(key : key);
#override
_AgendaOrganizeState createState() => _AgendaOrganizeState();
}
class _AgendaOrganizeState extends State<AgendaOrganize> {
CalendarView viewCalendar = CalendarView.month;
final CalendarController _controller = CalendarController();
#override
Widget build(BuildContext context) {
return Scaffold(
drawer: const MyMenu(),
appBar: AppBar(
title: const Center(
child: Text('Agenda')),
actions: <Widget>[
PopupMenuButton
(icon: const Icon(Icons.more_vert_outlined),
itemBuilder: (BuildContext context) {
return Menus.choice.map((String choice){
return PopupMenuItem(
value: choice,
child: Text(choice));
}).toList();
},
onSelected:
choiceMade,
),
IconButton(
icon: const Icon(
Icons.add_circle_outline,
color: Colors.white,
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const EventEditingPage()));
},
),
],),
body: CalendarWidget(viewCalendar),
//TODO //PROBLEME - SI J'AFFICHE PERSISTENTBOTTOMNAVBAR, affiche agenda FOR TODAY
// bottomNavigationBar: PersistentBottomNavBar(),
);
throw UnimplementedError();
}
#override
void setState(VoidCallback fn) {
viewCalendar = CalendarView.month;
super.setState(fn);
}
void choiceMade(String value) {
print(value);
setState(() {
viewCalendar = CalendarView.month;
});
}
}
class Menus {
static const List<String> choice = <String> [
'Day', 'Week', 'Work Week', 'Month','Schedule', 'Timeline Day', 'Timeline Week', 'Timeline Work Week'
];
}
Just add a key to the SfCalendar and it's going to change on every setState. Do it like the following:
Widget myCalendar(BuildContext context, CalendarView view) {
final events = Provider.of<EventProvider>(context).events;
final CalendarController _calendarControler = CalendarController();
return SfCalendar(
key: ValueKey(view), // <- Here
view: view,
...
Also, the CalendarWidget is passing the state further down to the _CalendarWidgetState itself. The _CalendarWidgetState should use widget.viewCalendar instead.
class CalendarWidget extends StatefulWidget {
CalendarView viewCalendar;
CalendarWidget(this.viewCalendar, {Key? key}) : super(key: key);
#override
State<CalendarWidget> createState() => _CalendarWidgetState();
}
class _CalendarWidgetState extends State<CalendarWidget> {
#override
Widget build(BuildContext context) {
return Column(
children: [
myCalendar(context, widget.viewCalendar),
const SizedBox(height: 8.0),
],
);
}
}
And here every choice possible:
void choiceMade(String value) {
setState(() {
switch (value) {
case 'Day':
viewCalendar = CalendarView.day;
break;
case 'Week':
viewCalendar = CalendarView.week;
break;
case 'Work Week':
viewCalendar = CalendarView.workWeek;
break;
case 'Month':
viewCalendar = CalendarView.month;
break;
case 'Schedule':
viewCalendar = CalendarView.schedule;
break;
case 'Timeline Day':
viewCalendar = CalendarView.timelineDay;
break;
case 'Timeline Week':
viewCalendar = CalendarView.timelineWeek;
break;
case 'Timeline Work Week':
viewCalendar = CalendarView.timelineWorkWeek;
break;
}
});
}
Based on the provided information, we have checked the mentioned issue “Calendar view not updating using Setstate”. View property of the SfCalendar is used to set the initial view of the calendar. For dynamic view changes, we have implemented the view property on the CalendarController. Kindly use that property from the controller for dynamic view changes. Please find the code snippet for dynamic view switching.
#override
void initState() {
_controller = CalendarController();
super.initState();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: SafeArea(
child: Column(
children: [
Container(
height: 550,
child: SfCalendar(
view: CalendarView.day,
controller: _controller,
),
),
RaisedButton(
onPressed: () => _controller.view = CalendarView.week,
child: Text('Change the view'),
),
],
),
),
)));
} ```
Also please find the breaking changes from the following changelog link.
Changelog link: https://pub.dev/packages/syncfusion_flutter_calendar/changelog
Also, you can use the allowed views property of the calendar for view navigation. Please find the UG from the following link.
UG link: https://help.syncfusion.com/flutter/calendar/date-navigations#allowed-views
We hope that this helps you. Please let us know if you need further assistance.
I have a very simply scenario. A button where when pushed would transition to the next screen. In Navigator 1.0, it is very simple to do by:
ElevatedButton(
onPressed: () {
// Navigate to next screen.
Navigator.push(
context,
MaterialPageRoute(builder: (context) => NextScreen()),
);
},
child: Text('Next Screen!'),
)
It seems that with Navigator 2.0 I would need to have a state to keep track of the current screen.
...
Navigator(
pages: [
MainScreenPageRoute()
if (state.isNextScreen) {
NextScreenPageRoute()
}
],
onPopPage: (route, result) {
// would have to keep track of this value
state.isNextScreen = false;
return route.didPop(result);
},
)
...
As for before I don't have to keep track of a state just to navigate, In Navigator 2.0 it seems that it is required. Is it really the case? If so do you guys have any suggestion of how to handle this properly?
P.S.
It also feels like now I have to keep track of the state which adds to more work compared to before.
yes in navigator 2 you change the page when you change the state.
There are some packages that help you to avoid this like qlevar_router
see this example
import 'package:flutter/material.dart';
import 'package:qlevar_router/qlevar_router.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
final books = [
Book('Stranger in a Strange Land', 'Robert A. Heinlein'),
Book('Foundation', 'Isaac Asimov'),
Book('Fahrenheit 451', 'Ray Bradbury'),
];
#override
Widget build(BuildContext context) => MaterialApp.router(
routeInformationParser: QRouteInformationParser(),
routerDelegate: QRouterDelegate([
QRoute(path: '/', builder: () => BooksListScreen(books)),
QRoute(
path:
'/books/:id([0-${books.length - 1}])', // The only available pages are the pages in the list
builder: () => BookDetailsScreen(books[QR.params['id']!.asInt!])),
]));
}
class Book {
final String title;
final String author;
Book(this.title, this.author);
}
class BooksListScreen extends StatelessWidget {
final List<Book> books;
BooksListScreen(this.books);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: ListView(
children: [
for (var book in books)
ListTile(
title: Text(book.title),
subtitle: Text(book.author),
onTap: () => QR.to('/books/${books.indexOf(book)}'))
],
),
);
}
}
class BookDetailsScreen extends StatelessWidget {
final Book book;
BookDetailsScreen(this.book);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(book.title, style: Theme.of(context).textTheme.headline6),
Text(book.author, style: Theme.of(context).textTheme.subtitle1),
],
),
),
);
}
}
don't forget to add qlevar_router: 1.4.0 in pubspec.yaml
I have below screen to implement, The Problem I am having is that I don't know how to show the snackbar at the bottom of the container. It's showing snackbar at the bottom of the screen, but i want it like below:
Note : The Snackbar in the image is shown with text 'Whoray, Right Answer.'
Adding Code :
SingleChildScrollView(
child: BlocBuilder<QuizControllerCubit, QuizControllerState>(
builder: (context, state) {
if (state is OnWrongAnswerGiven) {
SnackBarCustomWidget.showSnackBar(
context,
SnackBarWidget.getSnackBar(context, state.message),
);
} else if (state is OnRightAnswerGiven) {
SnackBarCustomWidget.showSnackBar(
context,
SnackBarWidget.getSnackBar(context, state.message),
);
}
return SnackBarCustomWidget(
child: Builder(
builder: (context) => Column(
mainAxisSize: MainAxisSize.min,
children: [
QuizTitleWidget(title: question.name),
Container(
constraints: BoxConstraints(
minHeight: MediaQueryUtils(context).height * 0.5,
maxHeight: MediaQue
let me give you a widget for that
class MySnackBarStack extends StatefulWidget {
final Widget child;
const MySnackBarStack({Key key, this.child}) : super(key: key);
#override
_MySnackBarStackState createState() => _MySnackBarStackState();
static void showSnackBar(BuildContext context,SnackBar snackBar){
context.findAncestorStateOfType<_MySnackBarStackState>()._showSnackBar(snackBar);
}
static void hideSnackBar(BuildContext context){
context.findAncestorStateOfType<_MySnackBarStackState>()._hideSnackBar();
}
}
class _MySnackBarStackState extends State<MySnackBarStack> {
Widget snackBar = SizedBox();
void _showSnackBar(SnackBar snackBar){
setState(() {
this.snackBar = snackBar;
});
}
void _hideSnackBar(){
setState(() {
snackBar = SizedBox();
});
}
#override
Widget build(BuildContext context) {
return Stack(
children: [
widget.child,
snackBar,
],
);
}
}
now Use it like this in any build method
//you cannot access MySnackBarStack here and up
MySnackBarStack(
child: Builder(
//you can only access MySnackBarStack here and down
builder: (newContext){
return MyContainer(
onTap:(){
MySnackBarStack.showSnackBar(newContext, snackBar);
}
);
},
),
)
can someone please help me how to make an onboarding screen after a custom splash screen be viewed only once by new users as it is very annoying to be viewed every time?
splashscreen and main codes are added below , a gif which is loaded for 3 seconds and then goes to boarding screen
My main code where it has its routes is attached below.
void main() async{
WidgetsFlutterBinding.ensureInitialized();
BusApp.sharedPreferences = await SharedPreferences.getInstance();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
print(BusApp.sharedPreferences
.getString('users'));
return StreamProvider<User>.value(
value: AuthService().user,
child: MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => CalculateRent()),
ChangeNotifierProvider(create: (_) => CardChanger()),
],
child: MaterialApp(
debugShowCheckedModeBanner: false, // Disables the debug ribbon
home:
SplashScreen(), // Shows splash screen as the first screen
routes: <String, WidgetBuilder>{
'/Intro_Slider': (BuildContext context) => new Boarding(),
'/Navig': (BuildContext context) => new Navig(),
'/Wrapper': (BuildContext context) => new Wrapper(),
'/Home': (BuildContext context) => new Home(),
'/Settings': (BuildContext context) => new Settings(),
}),
),
);
}
}
Splash Screen code :
class SplashScreenState extends State<SplashScreen> {
startTime() async {
var _duration = new Duration(seconds: 3);
return new Timer(_duration, navigationPage);
}
void navigationPage() {
Navigator.of(context).pushReplacementNamed('/Intro_Slider');
}
#override
void initState() {
super.initState();
startTime();
}
#override
Widget build(BuildContext context) {
return new Scaffold(
body: Container(
decoration: BoxDecoration(color: Colors.white),
child: Column(
children: <Widget>[
Expanded(
child: Center(
child: new Image.asset('assets/images/splashscreen/bus.gif'),
),
),
Align(
alignment: FractionalOffset.bottomCenter,
child: Padding(
padding: EdgeInsets.all(45.0),
child: Text(
"MoBis",
style: GoogleFonts.fredokaOne(
fontSize: 20,
),
),
)
)
],
),
),
);
}
}
You need to store a flag somewhere that the user has already seen the onboarding screen. For example, you can store that flag in your shared preferences.
Then when you would navigate after the splash screen you check this flag and depending on its value you go to the onboard or the home screen.
Something like this:
void navigationPage() {
if (BusApp.sharedPreferences.getBool('seenIntro')) {
Navigator.of(context).pushReplacementNamed('/Home');
} else {
BusApp.sharedPreferences.setBool('seenIntro', true);
Navigator.of(context).pushReplacementNamed('/Intro_Slider');
}
}
As you are already using shared pref. You can save a Boolean key 'isNewUser'. Show the onboarding screen if true else show HomeScreen.
And When show onboarding screen also update the isNewUser = false.
You need to save a boolean in your shared_preferences when user views the intro page for the first time. And you've to check for that bool in your splash screen and based on that you've to navigate to corresponding screens.
Splash Screen:
void navigationPage() {
var isExistingUser = BusApp.sharedPreferences.getBool('isExistingUser');
if (isExistingUser == null) {
Navigator.of(context).pushReplacementNamed('/Intro_Slider');
} else {
Navigator.of(context).pushReplacementNamed('/Home');
}
}
Intro Page:
Set the boolean to true, when user finish viewing the intro slider.
BusApp.sharedPreferences.setBool('isExistingUser', true);