i found a lot of solution passing data between stateless and stateful widget, but not between two stateful widgets
EDIT: i edited the code to show more details
MainPage
class MainPage extends StatefulWidget {
final String name;
MainPage({Key key, this.name}) : super(key: key);
#override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("Text from 2nd page -> "), //should return "Text from 2nd page -> BATMAN"
FloatingActionButton(
child: Icon(Icons.android),
onPressed: (){
Navigator.push(context, MaterialPageRoute(
builder: (context) => SecondPage()
));
},
),
],
),
),
);
}
}
SecondPage:
class SecondPage extends StatefulWidget {
SecondPage({Key key}) : super(key: key);
#override
_SecondPageState createState() => _SecondPageState();
}
class _SecondPageState extends State<SecondPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
...
Text("I choose ${_selectedMethod.name}"), // this returned "I choose BATMAN"
...
}
So basically i want to pass ${_selectedMethod.name} from 2ndPage to MainPage. sorry im so bad at explaining :(
You can pass variables back to a previous Page in the Navigator stack by sending it through the .pop() method and expecting them in the previous page with the .then() method:
class MainPage60643815 extends StatefulWidget {
#override
_MainPage60643815State createState() => _MainPage60643815State();
}
class _MainPage60643815State extends State<MainPage60643815> {
String displayTextFromSecondPage = '';
#override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Test from second page: '),
Text(displayTextFromSecondPage),
],
),
RaisedButton(
onPressed: goToSecondPage,
child: Text('Go to 2nd Page'),
),
],
),
);
}
void goToSecondPage(){
Navigator.of(context).push(MaterialPageRoute(
builder: (context) {
return SecondPage60643815(text: displayTextFromSecondPage);
}
)).then((valueFromSecondPage){
setState(() {
displayTextFromSecondPage = valueFromSecondPage;
});
});
}
}
class SecondPage60643815 extends StatefulWidget {
final String text;
SecondPage60643815({this.text});
#override
_SecondPage60643815State createState() => _SecondPage60643815State();
}
class _SecondPage60643815State extends State<SecondPage60643815> {
TextEditingController _textEditingController;
#override
void initState() {
_textEditingController = TextEditingController(
text: widget.text,
);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: goToFirstPage,
),
),
body: Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
TextFormField(
controller: _textEditingController,
autofocus: true,
),
],
),
),
),
);
}
void goToFirstPage(){
Navigator.of(context).pop(_textEditingController.text);
}
}
I assume that you have a SecondPage-Widget. So you can do something like:
class _MainPageState extends State<MainPage> {
#override
Widget build(BuildContext context) {
return Container(
child: SecondPage('BATMAN'),
);
}
}
This might print the desired "BATMAT" Text.
EDIT
This might be your SecondPage Widget:
class SecondPage extends StatefulWidget {
final String selection;
SecondPage(this.selection);
#override
_SecondPageState createState() => _SecondPageState();
}
class _SecondPageState extends State<SecondPage> {
#override
Widget build(BuildContext context) {
return Container(
child: Text(widget.selection),
);
}
}
Related
How to change a variable of a Widget from another widget?
This is the main stateful widget called HomePage:
class _HomePageState extends State<HomePage> {
#override
num counter = 0;
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Title")),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [Text(counter.toString()), CardWidget()],
),
));
}
}
This is CardWidget which is added to HomePage:
class CardWidget extends StatefulWidget {
const CardWidget({Key? key}) : super(key: key);
#override
_CardWidgetState createState() => _CardWidgetState();
}
class _CardWidgetState extends State<CardWidget> {
#override
Widget build(BuildContext context) {
return Card(
child: Column(
children: [
Text("Press the button to increment the counter"),
ElevatedButton(
onPressed: () {
//Something here to increment the counter in HomePage
},
child: const Text('Increment'),
),
],
));
}
}
This is what is shown on the screen:
Is it possible to create a connection between the two widgets: if I tap the button something happens in the HomePage Widget? (similar to delegate in UIKit)
You can pass Function parameter.
In your CardWidget add Function parameter.
class CardWidget extends StatefulWidget {
//Add clicked function
final Function onClicked;
const CardWidget({Key? key, required this.onClicked}) : super(key: key);
#override
_CardWidgetState createState() => _CardWidgetState();
}
class _CardWidgetState extends State<CardWidget> {
int _count = 0;
#override
Widget build(BuildContext context) {
return Card(
child: Column(
children: [
Text("Press the button to increment the counter"),
ElevatedButton(
onPressed: () {
//Something here to increment the counter in HomePage
//Execute `onClicked` and pass parameter you want
_count++;
widget.onClicked(_count);
},
child: const Text('Increment'),
),
],
));
}
}
then on HomePage add onClicked parameter
class _HomePageState extends State<HomePage> {
#override
num counter = 0;
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Fontanelle")),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(counter.toString()),
CardWidget(
//Add onClicked
onClicked:(count){
print("Clicked "+count.toString());
}
)
],
),
));
}
}
You have some solution for this case:
1, Create GlobalKey for StatefullWidget, and you can access to State from HomePage
2, Create a Stream from Homepage and pass to StatefullWidget
3, Pass param to StatefullWidget and use didUpdateWidget on state to listen.
I remember that was my first question when did my first step in flutter, how say #dangngocduc is true but my advice is that read about BLOC
https://pub.dev/packages/flutter_bloc
This is very helpful to do this.
I want to call a function from my StatefulWidget when a button in my StatelessWidget is pressed. In the following code you can see what I'm trying to do. I'm not really sure how it works, so maybe you can help me.
StatelessWidget - Button should call function from StatefulWidget
class HomeWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: Column(
children: [
SafeArea(child: Column(
children: [
Padding(
padding: EdgeInsets.all(10),
child: ProgressButton.icon(iconedButtons: {not needed}
onPressed: addUser,
state: ButtonState.idle),
),
],
)),
],
),
);
}
}
Statefulwidget - addUser() should be called by StatelessWidget
class HomeStateful extends StatefulWidget {
#override
_HomeStatefulState createState() => _HomeStatefulState();
}
class _HomeStatefulState extends State<HomeStateful> {
final firestoreInstance = FirebaseFirestore.instance;
CollectionReference users = FirebaseFirestore.instance.collection('users');
int _currentIndex = 0;
final List<Widget> _children = [
HomeWidget(),
MessageWidget(),
];
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: _children[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
onTap: onTabTapped,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text("Home"),
backgroundColor: Colors.blue,
),
BottomNavigationBarItem(
icon: Icon(Icons.mail),
title: Text("Nachrichten"),
backgroundColor: Colors.blue,
),
]),
);
}
void onTabTapped(int index) {
setState(() {
_currentIndex = index;
});
}
void addUser() {
firestoreInstance.collection("users").add({
"vorname": prenameController,
"nachname": nameController,
"geburtstag": birthdayController,
"adresse": adressController,
"telefon": numberController,
"id": 0
});
}
}
You can use callback function in Stateless widget, Like this
class HomeWidget extends StatelessWidget {
const HomeWidget({Key key, #required this.addUser}) : super(key: key);
/// If you want to pass any value back, then you can use something
/// like this
/// final Function(User user) addUser;
/// VoidCallback as name speaks its a empty fn.
final VoidCallback addUser; // ==> Here is the answer.
#override
Widget build(BuildContext context) {
return Container(
child: Column(
children: [
SafeArea(
child: Column(
children: [
Padding(
padding: EdgeInsets.all(10),
child: RaisedButton(
onPressed: addUser, // Add User fun here
)),
],
)),
],
),
);
}
}
And in stateful/parent widget.
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final _list = <Widget>[
HomeWidget(addUser: addUser) // Wrong, You cannot access the addUser here.
];
final _childList = <Widget>[];// But you can initialize here and add the HomeWidget in initState.
#override
void initState() {
super.initState();
_childList.addAll([
HomeWidget(addUser: addUser),
HomeWidget(addUser: addUser),
///... Other children
]);
}
void addUser() {
// Do Somethign
}
#override
Widget build(BuildContext context) {
return Column(
children: [
HomeWidget(addUser: addUser),
],
);
}
}
I have a stateful widget which is conditionally rendering two childs inside stack, and i want to change the condition of the rending from a third child . any idea ?
Parent code :
class MapPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body:Body()
);
}
}
class Body extends StatefulWidget {
final String showScreen;
const Body({
Key key,
this.showScreen="post",
}) : super(key:key);
#override
_BodyState createState() => _BodyState();
}
class _BodyState extends State<Body> {
Widget _conditionedWidget(){
if(this.widget.showScreen=="map"){
return MapScreen();
}else if(this.widget.showScreen == "post"){
return PostScreen();
}
}
#override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
DrawerScreen(),
_conditionedWidget(),
],
);
}
}
child code
class DrawerScreen extends StatefulWidget {
#override
_DrawerScreenState createState() => _DrawerScreenState();
}
class _DrawerScreenState extends State<DrawerScreen> {
#override
Widget build(BuildContext context) {
return Container(
color:kPrimaryColor,
padding:EdgeInsets.only(top:70),
child:Column(
children: <Widget>[
Row(
children: <Widget>[
SizedBox(width:20.0),
CircleAvatar(),
SizedBox(width:10.0),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('Biswas Sampad',style:TextStyle(
color:Colors.white,
fontWeight: FontWeight.bold,
fontSize: 20.0,
)),
Text('#biswassampad',style:TextStyle(
color:Colors.grey[200],
fontSize: 15.0,
))
],
)
],
),
Container(
padding: EdgeInsets.symmetric(horizontal: 20,vertical:20),
margin: EdgeInsets.symmetric(vertical:30),
child: Column(
children: <Widget>[
MenuButton(icon:Icons.style, name:'Explore',action:(){
print('showing maop');
}),
MenuButton(icon:Icons.tag_faces, name:'Profile',action:(){
print('showing profile');
}),
MenuButton(icon:Icons.people, name:'People',action:(){
print('showing People');
}),
MenuButton(icon:Icons.speaker_notes, name:'Messages',action:(){
print('showing messages');
}),
MenuButton(icon:Icons.notifications, name:'Notifications',action:(){
print('showing Notifications');
}),
MenuButton(icon:Icons.satellite,name:'Settings',action:(){
print('showing settings');
})
],
),
),
LogoutSection()
],
)
);
}
}
So basically i want to change the showScreen value of the parent widget from DrawerScreen>MenuButton>action ?
any idea how to do it !! Thanks in advance.
You can use the Function in "DrawerScreen" widget like this :
write this code into the header of the class :
final Function onChangeState = Function();
DrawerScreen({#rquired onChangeState});
and in MenuButton call onChangeState function , like this:
MenuButton(icon:Icons.satellite,name:'Settings',action:(){
widget.onChangeState("Settings");
})
and change old code in Body widget to :
#override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
DrawerScreen(onChangeState : (newState){
setState(){
this.widget.showScreen = newState;
};
}),
_conditionedWidget(),
],
);
}
I was able to pass the data widget.value from the FirstPage to SecondPage. There's a widget called thirdWidget inside SecondPage.
How do I pass widget.value to thirdWidget?
class FirstPage extends StatefulWidget {
#override
State<StatefulWidget> createState() => FirstPageState();
}
class FirstPageState extends State< FirstPage > {
final myController = TextEditingController();
#override
void dispose() {
myController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('First Route'),
),
body: Column(
children: <Widget>[
TextField(
controller: myController,
decoration: new InputDecoration(labelText: "Enter a number"),
keyboardType: TextInputType.number,
),
RaisedButton(
child: Text("show text"),
onPressed: () {
return Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ThirdRoute(
selectedDate: selectedDate,
value: myController.text,
)),
);
},
);
},
),
],
),
);
}
}
class SecondPage extends StatefulWidget {
final String value;
ThirdRoute({Key key, this.value})
: super(key: key);
#override
SecodpageState createState() => SecodpageState();
}
class SecodpageState extends State< SecondPage > {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Calendar Page"),
),
body: Column(
children: <Widget>[
Text("${widget.value}"),
Row(
children: thirdWidget(),
),
Center(
child: RaisedButton(
onPressed: () {
Navigator.pop(context);
},
child: Text('Go back!'),
),
),
],
),
);
}
}
List<Widget> thirdWidget() {
return Text("${widget.value}”)
}
Use this in your SecondPage
Row(
children: thirdWidget(widget.value),
)
And update your thirdWidget like:
List<Widget> thirdWidget(var data) {
// data is widget.value
return [];
}
Just pass that info into the state class. Something like that:
class SecondPage extends StatefulWidget {
final String value;
ThirdRoute({Key key, this.value})
: super(key: key);
#override
SecodpageState createState() => SecodpageState(value);
}
class SecodpageState extends State< SecondPage > {
final String value;
SecodpageState(this.value);
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Calendar Page"),
),
body: Column(
children: <Widget>[
Text("${widget.value}"),
Row(
children: thirdWidget(),
),
Center(
child: RaisedButton(
onPressed: () {
Navigator.pop(context);
},
child: Text('Go back!'),
),
),
],
),
);
}
}
List<Widget> thirdWidget() {
return Text(value);
}
I'm creating a music player and I need the music controls don't reinitialize or disappear on screen changing. If I add the code on another screen it will create another FloatingControls() widget.
I've already tried work with keys but that isn't the case because the Widget is recreated when I change screens.
As you can see my FloatingControls has a Widget called YoutubePlayer when I press play a video starts when I change screens I want that the player doesn't restart.
FloatingControls myFloatingControls = FloatingControls(key: Key("myFloatingControls"),);
class MusicSuggestions extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'MusicSuggestions',
home: new MainScreen(),
);
}
}
class MainScreen extends StatelessWidget {
const MainScreen({
Key key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
FlatButton(
child: Text("Change to Screen A"),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
ScreenA(floatingControls: myFloatingControls),
),
);
},
),
FlatButton(
child: Text("Change to Screen B"),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
ScreenB(floatingControls: myFloatingControls),
),
);
},
),
],
),
myFloatingControls
],
),
),
);
}
}
class ScreenA extends StatefulWidget {
final FloatingControls floatingControls;
const ScreenA({Key key, this.floatingControls}) : super(key: key);
#override
_ScreenAState createState() => _ScreenAState();
}
class _ScreenAState extends State<ScreenA> {
#override
Widget build(BuildContext context) {
return Scaffold(body: Center(child: widget.floatingControls,));
}
}
class ScreenB extends StatefulWidget {
final FloatingControls floatingControls;
const ScreenB({Key key, this.floatingControls}) : super(key: key);
#override
_ScreenBState createState() => _ScreenBState();
}
class _ScreenBState extends State<ScreenB> {
#override
Widget build(BuildContext context) {
return Scaffold(body: Center(child: widget.floatingControls,));
}
}
class FloatingControls extends StatefulWidget {
const FloatingControls({Key key}) : super(key: key);
#override
_FloatingControlsState createState() => _FloatingControlsState();
}
class _FloatingControlsState extends State<FloatingControls> {
VideoPlayerController _videoController;
bool isMute = false;
#override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
ClipOval(
child: Container(
width: 50,
height: 50,
child: YoutubePlayer(
autoPlay: false,
aspectRatio: 1,
width: 50,
context: context,
playerMode: YoutubePlayerMode.NO_CONTROLS,
source: "https://www.youtube.com/watch?v=PodZolu9v30",
quality: YoutubeQuality.LOW,
callbackController: (VideoPlayerController controller) {
_videoController = controller;
},
),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
IconButton(
icon: Icon(Icons.skip_previous),
onPressed: () {},
),
IconButton(
icon: _videoController == null ||
!_videoController.value.isPlaying
? Icon(Icons.play_arrow)
: Icon(Icons.pause),
onPressed: () {
setState(() {
_videoController == null ||
_videoController.value.isPlaying
? _videoController.pause()
: _videoController.play();
});
}),
IconButton(
icon: Icon(Icons.skip_next),
onPressed: () {},
),
Container(
width: 25,
height: 25,
)
],
),
]);
}
}
I expect to see my FloatingControls() in all screens without losing its state when I change pages.
Make sure to add floatingControls to your ScreenA and ScreenB build methods.
Example:
class _ScreenAState extends State<ScreenA> {
#override
Widget build(BuildContext context) {
return widget.floatingControls();
}
}
If you do this for both ScreenA and ScreenB, it should work.
--EDIT--
You should look into the PageStorage and PageStorageBucket classes, which will help you persist state across rebuilds. I don't have a lot of experience with these, so instead of giving you a potentially shoddy code snippet to copy, I will direct you to this tutorial by Tensor Programming (whose tutorials have helped me tremendously) which should help you do what you need to do. They are doing it with navigation bars, but it should extend easily to what you're doing.