I made a changeable theme in flutter and later I made multi-language support, I used getx for theme switching, I used easy_localization for language change. When I use GetMeterialapp on the main page, it doesn't work, when I do Meterialapp it works multi-language, but the theme change does not work. import 'package:get/get.dart' on another page; i am using and when i use easy_localization here easy_localization is not working. I'm canceling getx is working. I couldn't get out of this situation. If I can't solve it, I will either give up without changing the theme or multi-language support.
await EasyLocalization.ensureInitialized();
runApp(EasyLocalization(supportedLocales: [
Locale("en", "US"),
Locale("tr", "TR"),
], path: "assets/Language", saveLocale: true, child: MyApp()));
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Home Page',
localizationsDelegates: context.localizationDelegates,
supportedLocales: context.supportedLocales,
locale: context.locale,
theme: Themes.light,
darkTheme: Themes.dark,
themeMode: ThemeService().theme,
home: MyHomePage(),
debugShowCheckedModeBanner: false,
);
}
}
Initialize GetStorage inside main method.
void main() async{
WidgetsFlutterBinding.ensureInitialized();
await GetStorage.init();
runApp(MyApp());
}
Create storage object.
final storage = GetStorage();
Set a default value if storage is null.
storage.writeIfNull('darkmode', false);
Read the theme mode value.
bool isDarkMode = storage.read('darkmode');
Apply the theme mode.
Switch(
value: isDarkMode ,
onChanged: (value) => storage.write('darkmode', value),
)
Full code:
import 'package:flutter/material.dart';
import 'package:get_storage/get_storage.dart';
import 'package:get/get.dart';
void main() async{
WidgetsFlutterBinding.ensureInitialized();
await GetStorage.init();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
final storage = GetStorage();
#override
Widget build(BuildContext context) {
appdata.writeIfNull('darkmode', false);
return SimpleBuilder(
builder: (_)
{
bool isDarkMode = storage.read('darkmode');
return GetMaterialApp(
theme: isDarkMode ? ThemeData.dark() : ThemeData.light(),
home: Scaffold(
appBar: AppBar(title: Text("Getx Dynamic theme change"),),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(isDarkMode ? 'images/night.png' :'images/day.png' ,width: 100,height: 100,),
Switch(
value: isDarkMode ,
onChanged: (value) => storage.write('darkmode', value),
)
],
),
),
),
);
},
);
}
}
I am trying using the video_player package to try and load, play, and then replay a video from assets. The video loads correctly and plays fine on the first run through. However when I try to replay the video using _controller.seekTo(Duration.zero) and _controller.play() it remains frozen on the last frame of the video and won't replay back.
This occurs only in iOS but not in Android where it behaves as expected. And it only occurs for videos loaded from assets. If I load a video from a url using VideoPlayerController.network it also behaves as expected.
Below is the class I modified from the video_player example.
import 'package:video_player/video_player.dart';
import 'package:flutter/material.dart';
class VideoApp extends StatefulWidget {
#override
_VideoAppState createState() => _VideoAppState();
}
class _VideoAppState extends State<VideoApp> {
VideoPlayerController _controller;
#override
void initState() {
super.initState();
_controller = VideoPlayerController.asset('assets/video/MOV_GET_OUT_BED.mp4')
..initialize().then((_) {
// Ensure the first frame is shown after the video is initialized, even before the play button has been pressed.
setState(() {});
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Video Demo',
home: Scaffold(
body: Column(
children: <Widget>[
Center(
child: _controller.value.initialized
? AspectRatio(
aspectRatio: _controller.value.aspectRatio,
child: VideoPlayer(_controller),
)
: Container(),
),
GestureDetector(
onTap: () async {
await _controller.seekTo(Duration.zero);
setState( () {
_controller.play();
} );
},
child: Text("Restart video")
),
]
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
_controller.value.isPlaying
? _controller.pause()
: _controller.play();
});
},
child: Icon(
_controller.value.isPlaying ? Icons.pause : Icons.play_arrow,
),
),
),
);
}
#override
void dispose() {
super.dispose();
_controller.dispose();
}
}
To auto replay, add this to your initState() method.
_controller.setLooping(true);
I have a simple webview app built with flutter and flutter_webview_plugin library. I am trying to add push notification with firebase cloud messaging. The push notification works fine when the app is running in background or is switched off. If the app is on, I configured such that it shows an alert dialog. But this part does not work.
I removed the webview widget and instead added an appBar widget then hot reloaded the app. alert dialog was actually there working fine. It seems like the webview layer is above the alert dialog layer. So even though alert dialog did pop out correctly, the user cannot see it.
import 'package:flutter/material.dart';
import 'package:flutter_webview_plugin/flutter_webview_plugin.dart';
import 'dart:io';
import 'package:firebase_messaging/firebase_messaging.dart';
void main() => runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
home: MyApp(),
),
);
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
FirebaseMessaging _firebaseMessaging = FirebaseMessaging();
#override
void initState() {
super.initState();
firebaseCloudMessaging_Listeners();
}
void firebaseCloudMessaging_Listeners() {
_firebaseMessaging.getToken().then((token) {
print(token);
});
_firebaseMessaging.configure(
onMessage: (Map<String, dynamic> message) async {
print("onMessage: $message");
showDialog(
context: context,
builder: (context) => AlertDialog(
content: ListTile(
title: Text(message['notification']['title']),
subtitle: Text(message['notification']['body']),
),
actions: <Widget>[
FlatButton(
child: Text('Ok'),
onPressed: () => Navigator.of(context).pop(),
),
],
),
);
},
onResume: (Map<String, dynamic> message) async {
print('on resume $message');
},
onLaunch: (Map<String, dynamic> message) async {
print('on launch $message');
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
color: Colors.white,
child: SafeArea(
child: new WebviewScaffold(
url: "https://m.url.co.kr",
hidden: true,
),
),
),
);
}
}
You can first hide the webviewplugin using this line: webviewPlugin.hide(); and then run the code to create the alertdialog as such:
webviewPlugin.hide();
showDialog(
context: context,
builder: (context) => AlertDialog(
content: ListTile(
title: Text(message['notification']['title']),
subtitle: Text(message['notification']['body']),
),
actions: <Widget>[
FlatButton(
child: Text('Ok'),
onPressed: () => Navigator.of(context).pop(),
),
],
),
);
This is not possible with the flutter_webview_plugin package, you have to switch to the offical webview_flutter in order to do this...
The flutter_webview_plugin devs also give you this warning message:
Warning: The webview is not integrated in the widget tree, it is a native view on top of the flutter view. you won't be able to use snackbars, dialogs ...
Good luck
Im new to flutter. I would like to ask a question about my code. I have take a look on youtube and some google tutorial on this inkwell and on tap function to open new class activity on flutter.But the result is, when the image is tapped it open different image screen but they share same class file.
How can I have a separate page for different image click. For example,
I have five image in my flutter carousel slider.
Image 1 will open sliderpage 1. Image 2 will open sliderpage 2 and so on.Means they are on separate page instead of different image open same page but only show different images. Im trying this tutorial but they do have same page but different images displayed after on tap event is called. url https://www.youtube.com/watch?v=l9XOUoJsdy4
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
Widget image_carousel = new Container(
height: 345.0,
child: new Carousel(
boxFit: BoxFit.fill,
images: [
AssetImage('assets/s7.jpg'),
AssetImage('assets/s3.jpg'),
AssetImage('assets/s5.jpg'),
AssetImage('assets/s2.jpg'),
AssetImage('assets/s4.jpg'),
],
autoplay: true,
animationCurve: Curves.fastOutSlowIn,
animationDuration: Duration(milliseconds: 500),
dotColor: Colors.red[50],
dotSize: 4.0,
indicatorBgPadding: 2.0,
),
);
return Scaffold(
body: new Column(
children: <Widget>[
image_carousel,
//grid view
Container(
height:163.0,
child: Products(),
)
],
),
);
}
}
On this code, this code just display carousel image without any event on click is done , I was expecting to have different page routing by on tap event is happen when image assets is clicked and navigate to different pages.
First of all, you need to install carousel_slider, then create two screens:
The first one will contain carousel_slider when you click on the image it will navigate to the second screen and passing image URL of the image you clicked on, To have on tap event you need to wrap you Image widget with GestureDetector
import 'package:flutter/material.dart';
import 'package:carousel_slider/carousel_slider.dart';
import './image_screen.dart';
void main() => runApp(MaterialApp(home: Demo()));
class Demo extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<Demo> {
#override
Widget build(BuildContext context) {
Widget image_carousel = new Container(
height: 345.0,
child: CarouselSlider(
height: 400.0,
items: [
'http://pic3.16pic.com/00/55/42/16pic_5542988_b.jpg',
'http://photo.16pic.com/00/38/88/16pic_3888084_b.jpg',
'http://pic3.16pic.com/00/55/42/16pic_5542988_b.jpg',
'http://photo.16pic.com/00/38/88/16pic_3888084_b.jpg'
].map((i) {
return Builder(
builder: (BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width,
margin: EdgeInsets.symmetric(horizontal: 5.0),
decoration: BoxDecoration(color: Colors.amber),
child: GestureDetector(
child: Image.network(i, fit: BoxFit.fill),
onTap: () {
Navigator.push<Widget>(
context,
MaterialPageRoute(
builder: (context) => ImageScreen(i),
),
);
}));
},
);
}).toList(),
));
return Scaffold(
body: new Column(
children: <Widget>[
image_carousel,
],
),
);
}
}
The second screen will contain only the image you clicked on:
import 'package:flutter/material.dart';
class ImageScreen extends StatefulWidget {
final String url;
ImageScreen(this.url);
#override
_MyImageScreen createState() => _MyImageScreen(url);
}
class _MyImageScreen extends State<ImageScreen> {
final String url;
_MyImageScreen(this.url);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('ImageScreen'),
),
body: Image.network(url, width: double.infinity));
}
}
As I'm learning Flutter I've come to navigation. I want to pass data between screens similarly to passing data between Activities in Android and passing data between View Controllers in iOS. How do I do it in Flutter?
Related questions:
The best way to passing data between widgets in Flutter
Flutter pass data between widgets?
Flutter/ How to pass and get data between Statefulwidget
This answer will cover both passing data forward and passing data back. Unlike Android Activities and iOS ViewControllers, different screens in Flutter are just widgets. Navigating between them involves creating something called a route and using the Navigator to push and pop the routes on and off the stack.
Passing data forward to the next screen
To send data to the next screen you do the following things:
Make the SecondScreen constructor take a parameter for the type of data that you want to send to it. In this particular example, the data is defined to be a String value and is set here with this.text.
class SecondScreen extends StatelessWidget {
final String text;
SecondScreen({Key key, #required this.text}) : super(key: key);
...
Then use the Navigator in the FirstScreen widget to push a route to the SecondScreen widget. You put the data that you want to send as a parameter in its constructor.
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondScreen(text: 'Hello',),
));
The full code for main.dart is here:
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
title: 'Flutter',
home: FirstScreen(),
));
}
class FirstScreen extends StatefulWidget {
#override
_FirstScreenState createState() {
return _FirstScreenState();
}
}
class _FirstScreenState extends State<FirstScreen> {
// this allows us to access the TextField text
TextEditingController textFieldController = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('First screen')),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(32.0),
child: TextField(
controller: textFieldController,
style: TextStyle(
fontSize: 24,
color: Colors.black,
),
),
),
RaisedButton(
child: Text(
'Go to second screen',
style: TextStyle(fontSize: 24),
),
onPressed: () {
_sendDataToSecondScreen(context);
},
)
],
),
);
}
// get the text in the TextField and start the Second Screen
void _sendDataToSecondScreen(BuildContext context) {
String textToSend = textFieldController.text;
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondScreen(text: textToSend,),
));
}
}
class SecondScreen extends StatelessWidget {
final String text;
// receive data from the FirstScreen as a parameter
SecondScreen({Key key, #required this.text}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Second screen')),
body: Center(
child: Text(
text,
style: TextStyle(fontSize: 24),
),
),
);
}
}
Passing data back to the previous screen
When passing data back you need to do the following things:
In the FirstScreen, use the Navigator to push (start) the SecondScreen in an async method and wait for the result that it will return when it finishes.
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondScreen(),
));
In the SecondScreen, include the data that you want to pass back as a parameter when you pop the Navigator.
Navigator.pop(context, 'Hello');
Then in the FirstScreen the await will finish and you can use the result.
setState(() {
text = result;
});
Here is the complete code for main.dart for your reference.
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
title: 'Flutter',
home: FirstScreen(),
));
}
class FirstScreen extends StatefulWidget {
#override
_FirstScreenState createState() {
return _FirstScreenState();
}
}
class _FirstScreenState extends State<FirstScreen> {
String text = 'Text';
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('First screen')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(32.0),
child: Text(
text,
style: TextStyle(fontSize: 24),
),
),
RaisedButton(
child: Text(
'Go to second screen',
style: TextStyle(fontSize: 24),
),
onPressed: () {
_awaitReturnValueFromSecondScreen(context);
},
)
],
),
),
);
}
void _awaitReturnValueFromSecondScreen(BuildContext context) async {
// start the SecondScreen and wait for it to finish with a result
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondScreen(),
));
// after the SecondScreen result comes back update the Text widget with it
setState(() {
text = result;
});
}
}
class SecondScreen extends StatefulWidget {
#override
_SecondScreenState createState() {
return _SecondScreenState();
}
}
class _SecondScreenState extends State<SecondScreen> {
// this allows us to access the TextField text
TextEditingController textFieldController = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Second screen')),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(32.0),
child: TextField(
controller: textFieldController,
style: TextStyle(
fontSize: 24,
color: Colors.black,
),
),
),
RaisedButton(
child: Text(
'Send text back',
style: TextStyle(fontSize: 24),
),
onPressed: () {
_sendDataBack(context);
},
)
],
),
);
}
// get the text in the TextField and send it back to the FirstScreen
void _sendDataBack(BuildContext context) {
String textToSendBack = textFieldController.text;
Navigator.pop(context, textToSendBack);
}
}
This solution is very easy by passing variables in constructor:
first page:
Navigator.of(context).push(MaterialPageRoute(builder:(context)=>SecondPage('something')));
second page:
class SecondPage extends StatefulWidget {
String something;
SecondPage(this.something);
#override
State<StatefulWidget> createState() {
return SecondPageState(this.something);
}
}
class SecondPageState extends State<SecondPage> {
String something;
SecondPageState(this.something);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
//now you have passing variable
title: Text(something),
),
...
}
Get Perfect Solution :
From 1st Screen navigate to others as:
Navigator.pushNamed(context, "second",arguments: {"name" :
"Bijendra", "rollNo": 65210});
},
On Second Screen in build method get as :
#override
Widget build(BuildContext context) {
final Map<String, Object>rcvdData = ModalRoute.of(context).settings.arguments;
print("rcvd fdata ${rcvdData['name']}");
print("rcvd fdata ${rcvdData}");
return Scaffold(appBar: AppBar(title: Text("Second")),
body: Container(child: Column(children: <Widget>[
Text("Second"),
],),),);
}
Easiest way
FirstPage.dart
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PasswordRoute(usernameController)));
//usernameController is String value,If you want to pass multiple values add all
SecondPage.dart
class PasswordRoute extends StatefulWidget {
final String usernameController;//if you have multiple values add here
PasswordRoute(this.usernameController, {Key key}): super(key: key);//add also..example this.abc,this...
#override
State<StatefulWidget> createState() => _PasswordPageState();
}
class _PasswordPageState extends State<PasswordRoute> {
#override
Widget build(BuildContext context) {
...child: Text(widget.usernameController);
}
}
Answers above are useful for a small app, but if you want to remove the headache of continuously worrying about a widgets state, Google presented the Provider package.
https://pub.dev/packages/provider
Have a look into that one, or watch these videos from Andrea Bizzotto:
https://www.youtube.com/watch?v=MkFjtCov62g // Provider: The Essential Guide
https://www.youtube.com/watch?v=O71rYKcxUgA&t=258s // Provider: Introduction
Learn how to use the Provider package, and you are set for life :)
First Screen :
//send data to second screen
Navigator.push(context, MaterialPageRoute(builder: (context) {
return WelcomeUser(usernameController.text);
}));
Second Screen :
//fetch data from first screen
final String username;
WelcomeUser(this.username);
//use data to display
body: Container(
child: Center(
child: Text("Welcome "+widget.username,
textAlign: TextAlign.center,
),
),
),
Navigators in Flutter are similar to the Intent in Android.
There are two classes we are dealing with FirstScreen and SecondScreen.
In order to pass the data between the first screen to second do the following:
First of all add parameter in the SecondScreen class constructor
Now in the FirstScreen class provide the parameter
Navigator.push(context, MaterialPageRoute(builder: (context)=>SecondScreen(key_name:"Desired Data"));
So in the above line the "key_name" is the name of the parameter given in the SecondScreen class.
The "Desired Data" is data should be passed through the key to the SecondScreen class.
That's it you are done!!!
Passing Data to back screen flutter
Home Page
import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/container.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:sqflite_offline/View/Add_data.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
#override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
List<Method> items = []; // => List of items that come form next page.
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Hello"),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.of(context)
.push<Method>(MaterialPageRoute(builder: (_) => AddData()))
// fetching data form next page.
.then((value) => setState(() {
if (value?.title_Ctr != "" && value?.desc_Ctr != "") {
items.add(Method(
title_Ctr: value!.title_Ctr,
desc_Ctr: value.desc_Ctr));
}
}));
},
child: Icon(Icons.add),
),
body: items.isNotEmpty
? Column(children: [
Expanded(
child: ListView.builder(
itemCount: items.length,
itemBuilder: ((context, index) {
return Container(
margin:
EdgeInsets.only(top: 10, left: 10, right: 10),
padding: EdgeInsets.only(left: 10, right: 10),
height: 80,
decoration: BoxDecoration(
color: Colors.pinkAccent,
borderRadius: BorderRadius.circular(10)),
child: Center(
child: ListTile(
title: Text(items[index].title_Ctr),
subtitle: Text(items[index].desc_Ctr),
leading: Icon(Icons.emoji_people),
),
),
);
})))
])
: Center(
child: Text("No Record Found"),
));
}
}
Add List Page
import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/container.dart';
import 'package:flutter/src/widgets/framework.dart';
class AddData extends StatefulWidget {
const AddData({super.key});
#override
State<AddData> createState() => _AddDataState();
}
// Creating a Class and constructor.
class Method {
late String title_Ctr;
late String desc_Ctr;
Method({required this.title_Ctr, required this.desc_Ctr});
}
class _AddDataState extends State<AddData> {
// Creating a TextEditingController for two Fiends,
//one is for title TextField and second is for Description TextField.
TextEditingController titleCtr = TextEditingController();
TextEditingController descCtr = TextEditingController();
// Creating a Method for Passing a data to back page.
OnPressed(BuildContext context) {
var data = Method(title_Ctr: titleCtr.text, desc_Ctr: descCtr.text);
Navigator.pop(context, data);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Add Data")),
body: Form(child: Builder(builder: (context) {
return Column(children: [
TextFormField(
controller: titleCtr,
decoration: InputDecoration(hintText: "title"),
validator: (value) {
var newValue = value ?? "";
if (newValue.isEmpty) {
return 'title is Required';
}
return null;
},
),
TextFormField(
controller: descCtr,
decoration: InputDecoration(hintText: "Description"),
validator: (value) {
var newValue = value ?? "";
if (newValue.isEmpty) {
return 'Discription is Required';
}
return null;
},
),
MaterialButton(
color: Colors.red,
onPressed: () {
if (Form.of(context)?.validate() ?? false) {
OnPressed(context);
}
},
child: Text("Save"),
)
]);
})));
}
}
screenshot
1) From where you want to push :
onPressed: () async {
await Navigator.pushNamed(context, '/edit',
arguments: userData);
setState(() {
userData = userData;
});}
2) From Where you want to pop :
void updateData() async{
WorldTime instance = locations;
await instance.getData();
Navigator.pop(context, userData);
}
If you use get package then try this . passing data with get package
check get package package link
Here's another approach.
Nothing wrong with the other answers. I've tried all of the methods mentioned using global wide widgets like provider, third-party solutions, Navigator arguments, etc. This approach differs by allowing one to chain calls and pass precise data of any type required to the widget using it. We can also gain access to a completion handler event and can use this technique without being constrained to Navigator objects.
Here's the tldr:
tldr; We have to turn our thinking on its head a bit. Data can be
passed to the called widget when you navigate to it by using final
arguments with default values in the destination widget. Using an
optional function you can get data back from the 'child' (destination)
widget.
The complete explanation can be found using this SO answer., (Gist)
I just want to be here to help that 1% who might go through what I did Lol
Don't forget to put an "await" infront of "Navigator.push" in the first page,
otherwise no data will be returned to the first page when you pop from the second page...
Passing Data to back screen flutter
First Screen
final result = await Navigator.of(context).push(MaterialPageRoute(builder: (context)=>const PaymentScreen()));
Second Screen
String selected = "Credit/Debit";
Navigator.pop(context,selected);