class _SplashScreenState extends State<SplashScreen> {
void init(){
loadDatas();
super.initState();
}
Future<void> loadDatas()async{
await Future.delayed(Duration(seconds:2));
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Image.asset('assets/todo3.png'),
),
);
}
}
SplashScreen Widget I created.
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
home: const SplashScreen(),
);
}
}
The part where I call SplashScreen in Main.dart. Am I adding the image in the wrong place? Or should I not make SplashScreen my homepage?
Instead of adding dependency for splash screen, it will better to create a screen for splash and navigate from that screen.
Steps for Creating a Splash Screen
Make a new screen.
On the init method of the splash screen, call a async function and add the necessary code to execute in the splash screen.
#override
void initState(){
loadDatas();
super.initState();
}
Future<void> loadDatas()async{
await Future.Delayed(Duration(seconds:2));
}
Related
My question is how to show a splash screen or alternatively show a loading screen if I execute the following code:
void main() async{
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
You can simply use Future Builder for that,
void main() async{
WidgetsFlutterBinding.ensureInitialized();
//remove this line from your code
//await Firebase.initializeApp();
runApp(MyApp());
}
this is code for MyApp()
class MyApp extends StatelessWidget {
final Future<FirebaseApp> _initialization = Firebase.initializeApp();
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: FutureBuilder(
future: _initialization,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return LoginWrapper();
}
return SplashScreen();
},
),
);
}
}
it shows your SplashScreen while loading your firebase App ;)
So I have a very simple application.
In Main I initialize Admob and call Root Widget.
From Root I show a Stateful Widget "Home".
From Home Call the Notification Logic.
When you "click" on the notification I send you again to Root.
When I first run the application, the adMobBanner works good.
After you receive the notification and you click on it, the app starts again, everything works as expected but the adMobBanner is a black banner now.
I think the Admob is not initialized properly. I tried to "Admob.initialize("appId")" in Root, in LocalNotification widget, everywhere, but still the same result.
Any ideas?
void main() async {
Admob.initialize("appId");
runApp(Root());
}
class Root extends StatelessWidget {
final String title = "Title";
Root();
#override
Widget build(BuildContext context) {
return MaterialApp(
title: title,
),
home: Home(title: title));
}
}
class Home extends StatefulWidget {
final String title;
Home({this.title});
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
#override
Widget build(BuildContext context) {
final adMobBanner = AdmobBanner(adUnitId: "id", adSize: AdmobBannerSize.BANNER);
return Scaffold(
appBar: appBar,
body: Column(
children: <Widget>[
Container(),
adMobBanner,
LocalNotification()
],
),
);
}
}
class LocalNotification extends StatefulWidget {
#override
_LocalNotificationState createState() => _LocalNotificationState();
}
class _LocalNotificationState extends State<LocalNotification> {
void initState() {
super.initState();
//initialization notification plugin
showNotification(); //the notification with onSelectNotification callback
}
Future onSelectNotification(String payload) async {
await Navigator.push(context, MaterialPageRoute(builder: (context) {
return Root(); // send you to Root again
}));
}
#override
Widget build(BuildContext context) {
return Container();
}
}
Code I show you is the simplified code which I'm troubled in.
My expected result is [1,2,3,4,5,6], but app says [1,2,3].
I know "loadMoreInterger()" should be in "initState()", but for some reason I have to put it in Widget build() {"HERE"}.
I wonder if why doesn't it work, and the solution for correct result.....
I really appreciate for your help :)
import 'package:flutter/material.dart';
import 'dart:async';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#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();
}
// ↓↓↓↓↓↓↓↓↓↓↓WHERE I CANNOT UNDERSTAND↓↓↓↓↓↓↓↓↓↓↓
class _MyHomePageState extends State<MyHomePage> {
List<int> intList = [1,2,3];
Future<List<int>> loadMoreInteger() async {
print('Future');
return [4,5,6];
}
#override
Widget build(BuildContext context) {
loadMoreInteger().then((value) {
intList.addAll(value); // why doesn't it work?
});
print("console: $intList");
return Scaffold(
body: Center(
child: Text("display: $intList")
)
);
}
}
//Expected result: [1,2,3,4,5,6]
//Actual result: [1,2,3]
put it in initState override function and it works for yu !!!!
List<int> intList = new List();
Future<List<int>> loadMoreInteger() async {
print('Future');
return [4,5,6];
}
#override
void initState() {
super.initState();
intList = [1,2,3];
loadMoreInteger().then((v){
setState(() {
intList.addAll(v) ;
});
}); }
Here is what your build method does: after entering the method it starts to execute loadMoreInteger() future. Afterwards even if executed future is synchronous it only schedules call of next future that is produced by calling .then. So build method continues to execute with old intList value. And [4,5,6] will be added only after build completes.
In general you can wait for future to complete by calling it with await keyword. But build method is overriden and already has predefined return type that is not future, so you can not call await inside build.
What you can do:
I highly recommend moving any manipulation with data from build method. Its purpose is to produce widgets as fast as possible. It can be called multiple times at some moment unexpected for developer.
One of possible options for you will be moving loadMoreInteger() to initState and calling setState when intList is updated
#override
void initState() {
super.initState();
loadMoreInteger().then((value) {
setState(() {
intList.addAll(value);
});
});
}
In my example app below, I have two routes: HomeRoute and OtherRoute. I want OtherRoute to always return a result when it is popped from the navigator. In this example, it does this when the RaisedButton in OtherRoute is pressed. However, I also want it to return a result when the back button is pressed. I know I can override the back button on the AppBar, but I also want to override the behavior of the "system" back button on Android.
I know I can use WillPopScope to prevent the system back button from popping the current route, but I do not want this. If possible, I want the system back button to still be able to pop the current route, just with a result included. Is this possible?
Here is my sample app:
import 'package:flutter/material.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: 'Test',
home: Scaffold(
appBar: AppBar(title: Text("Home"),),
body: HomeRoute(),
),
);
}
}
class HomeRoute extends StatelessWidget{
#override
Widget build(BuildContext context) {
return RaisedButton(onPressed: (){
Navigator.push(context, MaterialPageRoute(builder: (context){
return OtherRoute();
})).then((result){
// I want result here to never be null
Scaffold.of(context).showSnackBar(SnackBar(content: Text("$result")));
});
});
}
}
class OtherRoute extends StatelessWidget{
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Other route"),),
body: RaisedButton(onPressed: (){
Navigator.pop(context, "Result!");
})
);
}
}
Wrap your scaffold with WillPopScope in OtherRoute and override onWillPop method as follows:
#override
Widget build(BuildContext context) {
return WillPopScope(
//....
),
onWillPop: () async {
Navigator.pop(context, "Result!");
return false;
},
);
}
I'm using flutter video_player(https://pub.dartlang.org/packages/video_player) plugin to play videos. But When I navigate from one page to another in flutter the video is still playing. I'm having the same problem when playing music from any other app(eg: playing music from notification while flutter video is playing). How can I pause it?
Edit:
Second option:
So I came up with another solution. VisibilityDetector
is a `Widget' that can wrap any other Widget and notify when the visible area of the widget changed.
VisibilityDetector(
key: Key("unique key"),
onVisibilityChanged: (VisibilityInfo info) {
debugPrint("${info.visibleFraction} of my widget is visible");
if(info.visibleFraction == 0){
videoPlayerController.pause();
}
else{
videoPlayerController.play();
}
},
child: VideoPlayer());
)
First Option:
By using the RouteObserver and RouteAware your widget will be informed whenever another screen is pushed on top of it, or it's popped back on top.
First, you need to add a RouteObserver to MaterialApp or your Navigator.
RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>();
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
navigatorObservers: [routeObserver], //HERE
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
Then you must add the RouteAware mixin to your widget, and subscribe it to the RouteObserver.
class VideoPlayerItem extends StatefulWidget {
final File file;
VideoPlayerItem(this.file);
#override
_VideoPlayerItemState createState() => _VideoPlayerItemState();
}
class _VideoPlayerItemState extends State<VideoPlayerItem> with RouteAware {
VideoPlayerController _videoPlayerController;
#override
void initState() {
super.initState();
_videoPlayerController = VideoPlayerController.file(widget.file);
_videoPlayerController.initialize().then((_) {
if (mounted) {
setState(() {});
_videoPlayerController.play();
}
});
}
#override
void didChangeDependencies() {
routeObserver.subscribe(this, ModalRoute.of(context));//Subscribe it here
super.didChangeDependencies();
}
/// Called when the current route has been popped off.
#override
void didPop() {
print("didPop");
super.didPop();
}
/// Called when the top route has been popped off, and the current route
/// shows up.
#override
void didPopNext() {
print("didPopNext");
_videoPlayerController.play();
super.didPopNext();
}
/// Called when the current route has been pushed.
#override
void didPush() {
print("didPush");
super.didPush();
}
/// Called when a new route has been pushed, and the current route is no
/// longer visible.
#override
void didPushNext() {
print("didPushNext");
_videoPlayerController.pause();
super.didPushNext();
}
#override
Widget build(BuildContext context) {
return _videoPlayerController?.value?.initialized ?? false
? AspectRatio(
aspectRatio: _videoPlayerController.value.aspectRatio,
child: VideoPlayer(_videoPlayerController),
)
: Container();
}
#override
void dispose() {
routeObserver.unsubscribe(this); //Don't forget to unsubscribe it!!!!!!
super.dispose();
_videoPlayerController.dispose();
}
}
You have - VideoPlayerController _controller;
Before Navigating to other screen -
_controller.pause(); // add this
Navigator.push(
context, MaterialPageRoute(builder: (BuildContext context) => NextPage()));
OR
if it's a Stateful widget then you can override - dispose() method.
#override
void dispose() {
super.dispose();
_controller.pause();
}
Simple and its work for me, I get this from chewie example code: https://pub.dev/packages/chewie/example
Just on the onTap/onPressed use setState and in this setState make videoController.pause or dispose, whatever you want.
onTap:() {
setState(() {
widget.videoController.pause();
Navigator.of(context)
.pushNamed(YOUR-NAVIGATION-PAGE-NAME);
});
},