About passing data in flutter - flutter

I have List String that i want to pass to another screen using push in flutter.
List<String> example;
example : [
'a',
'b',
'c',
]
And i want to pass it to another List String named lagu in another screen using push.
I'm already trying using
Navigator.push(context, MaterialPageRoute(builder: (context){
return ScreenB(lagu: example);))}
But it give me one string which is everything inside example as single string when i check inside of lagu using print(lagu)
When i check print(lagu[0]) it give me '[' in console
any solution?

Try as follows:
Navigator.push(
context,
MaterialPageRoute(builder: (context) => HomePage(lagu:example)),
);
HomePage class
class HomePage extends StatefulWidget {
final List<String>? lagu;
const HomePage({this.lagu});
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<HomePage> {
#override
void initState() {
super.initState();
print(widget.lagu);
}
#override
Widget build(BuildContext context) {
return Scaffold(body: Text(widget.lagu![0].toString()));
}
}

you can solve the problem by passing the data using argument
you can pass the data like:
MaterialPageRoute(
builder: (context) => const ScreenB(),
settings: RouteSettings(
arguments: example,
),
and in your widget that is receiving the data (ScreenB) you can read the data like:
List recievedExample = ModalRoute.of(context)!.settings.arguments as List;

I have created working solution, everything works fine so you can adjust it.
class ScreenA extends StatefulWidget {
const ScreenA({Key? key}) : super(key: key);
#override
State<ScreenA> createState() => _ScreenAState();
}
class _ScreenAState extends State<ScreenA> {
List<String> example = [
'a',
'b',
'c',
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: ElevatedButton(
child: Text('ScreenB'),
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (ctx) => ScreenB(lagu: example)));
},
),
),
);
}
}
class ScreenB extends StatefulWidget {
final List<String> lagu;
ScreenB({Key? key, required this.lagu}) : super(key: key);
#override
State<ScreenB> createState() => _ScreenBState();
}
class _ScreenBState extends State<ScreenB> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: widget.lagu.map((e) => Text(e)).toList(),
),
),
);
}
}

Related

How to send arguments in restorablePush? (flutter)

The flutter docs on restorablePush says that "Any object that is serializable via the StandardMessageCodec can be passed as arguments. Often, a Map is used to pass key-value pairs." But I am struggling to do this, as the route builder needs to be static.
I've made a small example below that illustrates the problem - basically, I want to be able to pass title as an argument from Screen1 to Screen2 and display it in the AppBar. Not sure where and how to put the argument in.
class Screen1 extends StatefulWidget {
const Screen1({Key? key}) : super(key: key);
#override
State<Screen1> createState() => _Screen1State();
}
class _Screen1State extends State<Screen1> {
String title = 'blah';
static Route<void> _myRouteBuilder(BuildContext context, Object? arguments) {
return MaterialPageRoute<void>(
builder: (BuildContext context) => const Screen2(),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: TextButton(
onPressed: () {
Navigator.restorablePush(context, _myRouteBuilder);
// Put somewhere... arguments: {'appBarTitle': title}
},
child: const Text('Go to Screen 2')),
),
);
}
}
class Screen2 extends StatefulWidget {
const Screen2({Key? key}) : super(key: key);
#override
State<Screen2> createState() => _Screen2State();
}
class _Screen2State extends State<Screen2> {
String _string = '';
#override
Widget build(BuildContext context) {
final args =
ModalRoute.of(context)!.settings.arguments as Map<String, String>;
_string = args['appBarTitle'] ?? 'error';
return Scaffold(
appBar: AppBar(title: Text(_string)),
);
}
}
You need to add the settings parameter to MaterialPageRoute so that ModalRoute receives the argument on screen 2.
class Screen1 extends StatefulWidget {
const Screen1({Key? key}) : super(key: key);
#override
State<Screen1> createState() => _Screen1State();
}
class _Screen1State extends State<Screen1> {
String title = 'blah';
static Route<void> _myRouteBuilder(BuildContext context, Object? arguments) {
return MaterialPageRoute<void>(
settings: RouteSettings(name: '/screen2', arguments: arguments),
builder: (BuildContext context) => const Screen2(),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: TextButton(
onPressed: () {
Navigator.restorablePush(context, _myRouteBuilder, arguments: {'appBarTitle': title});
// Put somewhere... arguments: {'appBarTitle': title}
},
child: const Text('Go to Screen 2')),
),
);
}
}
class Screen2 extends StatefulWidget {
const Screen2({Key? key}) : super(key: key);
#override
State<Screen2> createState() => _Screen2State();
}
class _Screen2State extends State<Screen2> {
String _string = 'Test';
#override
Widget build(BuildContext context) {
final args =
ModalRoute.of(context)?.settings.arguments;
if(args is Map){
_string = args['appBarTitle'] ?? 'error';
}
return Scaffold(
appBar: AppBar(title: Text(_string)),
);
}
}

How to change state of page builded with Navigator

i tried to change the state of Page2 builded from HomePage with Navigator and MaterialPageRoute but the state didn't change. it seems that i can't change the state of another page, is there any solution to change the state of th Page2 from HomePage?
NB: i changed the page before the delay done.
HomePage :
class MyHomePage extends StatefulWidget {
const MyHomePage({
Key? key,
}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
var text = 'pp';
#override
void initState () {
super.initState();
_changeStateAfterDelay();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Text("ggg"),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _showPages,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
void _showPages() {
Navigator.of(context).push(MaterialPageRoute(
maintainState: false,
builder: (context) => Page2(title: text)
));
}
void _changeStateAfterDelay() {
Future.delayed(const Duration(milliseconds: 10000), () {
setState(() {
text += "tt";
});
});
}
}
Page2:
class Page2 extends StatefulWidget {
final String title;
const Page2({
Key? key,
required this.title,
}) : super(key: key);
#override
_Page2State createState() => _Page2State();
}
class _Page2State extends State<Page2> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text(widget.title)
),
);
}
}
One possible way to change the state of Page2 from Page1 (in your case HomePage), it is to use a state manager like Provider.
If you want to change the state of Page1 from Page2, you can use a Callback set to Page2 as a parameter.

Flutter NullSafety cannot push Page after showing and change modalProgressHUD

I'm using Provider package to expose a simple boolean variabile that allow to change the status of variable "inAsyncCall" of the ModalProgressHUD widget.
When i try to do somethings before navigate to another page, and i want to display the circlular progress indicator during that computation, when the Future terminated, the current widget has been disposed and i cannot use Navigator.push():
Unhandled Exception: This widget has been unmounted, so the State no longer has a context (and should be considered defunct).
Consider canceling any active work during "dispose" or using the "mounted" getter to determine if the State is still active.
this is my Provider with ChangeNotifier class:
class CartProvider with ChangeNotifier {
bool _inAsync = false;
bool get inAsync => _inAsync;
void setInAsync(bool flag) {
this._inAsync = flag;
notifyListeners();
}
}
I inject the provider before the MaterialApp widget like this:
void main() async {
runApp(App());
}
class App extends StatefulWidget {
#override
_AppState createState() => _AppState();
}
class _AppState extends State<App> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (context) => CartProvider(),
)
],
child: MaterialApp(
home: HomePage(),
),
);
}
}
And this is the simple home page where i access via context the provider injected:
class HomePage extends StatefulWidget {
HomePage({Key? key}) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
final cartProvider = context.watch<CartProvider>();
return ModalProgressHUD(
inAsyncCall: cartProvider.inAsync,
child: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('HOME'),
FirstStatefulWidget(),
],
),
),
bottomNavigationBar: SecondStatefulWidget(),
),
);
}
}
class FirstStatefulWidget extends StatefulWidget {
FirstStatefulWidget({Key? key}) : super(key: key);
#override
_FirstStatefulWidgetState createState() => _FirstStatefulWidgetState();
}
class _FirstStatefulWidgetState extends State<FirstStatefulWidget> {
late CartProvider cartProvider = context.read<CartProvider>();
Future doSomething() async {
cartProvider.setInAsync(true);
await Future.delayed(
Duration(seconds: 2),
() => {
cartProvider.setInAsync(false),
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondPage(),
),
)
},
);
}
#override
Widget build(BuildContext context) {
return Container(
child: ElevatedButton(
child: Text('do call'),
onPressed: doSomething,
),
);
}
}
class SecondStatefulWidget extends StatefulWidget {
SecondStatefulWidget({Key? key}) : super(key: key);
#override
_SecondStatefulWidgetState createState() => _SecondStatefulWidgetState();
}
class _SecondStatefulWidgetState extends State<SecondStatefulWidget> {
late CartProvider cartProvider = context.read<CartProvider>();
void goToAnotherPageAfterCall() async {
try {
cartProvider.setInAsync(true);
Future.delayed(
Duration(seconds: 2),
() => {
cartProvider.setInAsync(false),
Navigator.push(
context,
new MaterialPageRoute(
builder: (context) => SecondPage(),
),
)
},
);
} on Exception catch (e) {
cartProvider.setInAsync(false);
}
}
#override
Widget build(BuildContext context) {
return Container(
child: ElevatedButton(
child: Text('goToAnotherPage'),
onPressed: goToAnotherPageAfterCall,
),
);
}
}

How to setState widget by other widget Flutter ,simplecode below

right widget has gesterdetector that adds a String ("ZzZ") to List;
left widget shows all String there in String list by List view Buildder,
right widget adds "ZzZ" to list after pressing the button successfully but it dosent sets ui state...
in android studio after hot reload it shows all added "ZzZ"
import 'package:flutter/material.dart';
List<String> ListOfZzZ=[];
class homescreen extends StatefulWidget {
#override
_homescreenState createState() => _homescreenState();
}
class _homescreenState extends State<homescreen> {
#override
Widget build(BuildContext context) {
return Material(
child: Scaffold(
body: Row(children: [
Expanded(child:RightSidewidget()),
Expanded(child:LeftSidewidget())
],
)),
);
}
}
class RightSidewidget extends StatefulWidget {
#override
_RightSidewidgetState createState() => _RightSidewidgetState();
}
class _RightSidewidgetState extends State<RightSidewidget> {
#override
Widget build(BuildContext context) {
return GestureDetector(
child: Container(child:Text("add new ZzZ"),),
**onTap: (){
setState(() {
ListOfZzZ.add("ZzZ");
});},);**
}
}
class LeftSidewidget extends StatefulWidget {
#override
_LeftSidewidgetState createState() => _LeftSidewidgetState();
}
class _LeftSidewidgetState extends State<LeftSidewidget> {
#override
Widget build(BuildContext context) {
return Container(child:
ListView.builder(
itemCount: ListOfZzZ.length,
itemBuilder: (context,index)=>Text(ListOfZzZ[index])),);
}
}
check the Provider package it can help you achieve what you want, ere is a really good tutorial by the flutter devs showing how to use manage the state of your app and notify widgets of the changes other widgets have.
setState rebuild in very specyfic way. you can read about this in here:
https://api.flutter.dev/flutter/widgets/State/setState.html
in simple world setState call the nearest build (I think this is not full true, but this intuitions works for me)
In your code when you tap right widget and call setState only rightwidget will be rebuild.
So this is the easy solutions:
Make left and right widget statless.
In homescreen in row add gestureDetector(or textButton like in my example) and here call setState. When you do that, all homeSreen will be rebuild so left and right widget too. and your list will be actual. Here is example:
List<String> ListOfZzZ = [];
class homescreen extends StatefulWidget {
#override
_homescreenState createState() => _homescreenState();
}
class _homescreenState extends State<homescreen> {
#override
Widget build(BuildContext context) {
return Material(
child: Scaffold(
body: Row(
children: [
Expanded(
child: TextButton(
onPressed: () => setState(() {
ListOfZzZ.add("ZzZ");
}),
child: RightSidewidget())),
Expanded(child: LeftSideWidget())
],
)),
);
}
}
class RightSidewidget extends StatelessWidget {
const RightSidewidget({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
color: Colors.amber[50],
child: Text("add new ZzZ"),
);
}
}
class LeftSideWidget extends StatelessWidget {
const LeftSideWidget({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
child: ListView.builder(
itemCount: ListOfZzZ.length,
itemBuilder: (context, index) => Text(ListOfZzZ[index])),
);
}
}
The hard way, but more elegant and better is to use some state manager like bloc. Here is official site: https://bloclibrary.dev/#/gettingstarted
there is a lot of tutorials and explanations. But this is not solutions for 5 minutes.
Edit: I make some solution with BLoC. I hope this help. I use flutter_bloc and equatable packages in version 7.0.1
void main() {
EquatableConfig.stringify = kDebugMode;
Bloc.observer = SimpleBlocObserver();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
title: Text('myList'),
),
body: BlocProvider(
create: (context) => MylistBloc()..add(AddToList('Start')),
child: Row(
children: [
Expanded(flex: 1, child: buttonsPanel()),
Expanded(flex: 1, child: ListOfZzZ()),
],
),
),
),
);
}
}
class ListOfZzZ extends StatefulWidget {
const ListOfZzZ({Key? key}) : super(key: key);
#override
_ListOfZzZState createState() => _ListOfZzZState();
}
class _ListOfZzZState extends State<ListOfZzZ> {
late MylistBloc _mylistBloc;
#override
Widget build(BuildContext context) {
return BlocBuilder<MylistBloc, MylistState>(
//builder: (context, state) {return ListView.builder(itemBuilder: (BuildContext context,int index){return ListTile(title: state.positions[index];)},);},
builder: (context, state) {
if (state.positions.isEmpty) {
return const Center(child: Text('no posts'));
} else {
return ListView.builder(
itemBuilder: (BuildContext context, int index) {
return ListTile(title: Text(state.positions[index]));
},
itemCount: state.positions.length,
);
}
},
);
}
}
class buttonsPanel extends StatefulWidget {
const buttonsPanel({Key? key}) : super(key: key);
#override
_buttonsPanelState createState() => _buttonsPanelState();
}
class _buttonsPanelState extends State<buttonsPanel> {
late MylistBloc _mylistBloc;
#override
void initState() {
super.initState();
_mylistBloc = context.read<MylistBloc>();
}
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
TextButton(
onPressed: () => {_mylistBloc.add(AddToList('Spam'))},
child: Text('Spam')),
TextButton(
onPressed: () => {_mylistBloc.add(AddToList('Ham'))},
child: Text('Ham')),
],
);
}
class SimpleBlocObserver extends BlocObserver {
#override
void onTransition(Bloc bloc, Transition transition) {
super.onTransition(bloc, transition);
print(transition);
}
#override
void onError(BlocBase bloc, Object error, StackTrace stackTrace) {
print(error);
super.onError(bloc, error, stackTrace);
}
}
class MylistState extends Equatable {
final List<String> positions;
final int lenght;
const MylistState({this.positions = const <String>[], this.lenght = 0});
#override
List<Object> get props => [positions];
#override
String toString() => 'Lenght: {$lenght} Positions: {$positions}';
#override
MylistState copyWith(List<String>? positions) {
return MylistState(positions: positions ?? this.positions);
}
}
abstract class MylistEvent extends Equatable {
const MylistEvent();
#override
List<Object> get props => [];
}
class AddToList extends MylistEvent {
final String posToAdd;
#override
AddToList(this.posToAdd);
}
class MylistBloc extends Bloc<MylistEvent, MylistState> {
MylistBloc() : super(MylistState(positions: const <String>[]));
#override
Stream<MylistState> mapEventToState(
MylistEvent event,
) async* {
if (event is AddToList) {
yield await _mapListToState(state, event.posToAdd);
}
}
Future<MylistState> _mapListToState(
MylistState state, String posToAdd) async {
List<String> positions = [];
positions.addAll(state.positions);
positions.add(posToAdd);
return MylistState(positions: positions, lenght: positions.length);
}
}
}

Passing data from modal through multiple widgets

Another newbe to Flutter here. I really thought this will be 2 min job but I actually get stack here. I need to pass 'Hello' data
from modal widget
class ToolModal extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Text('tools'),
FlatButton(
child: Text('save'),
onPressed: () => Navigator.pop(context, 'Hello'),
),
],
);
}
}
through Bar widget
class Bar extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BarItem(
name: 'Option1',
icon: Icons.category,
onPressed: () {
showModalBottomSheet(
isScrollControlled: true,
context: context,
builder: (context) => ToolModal(),
);
},
);
}
}
to my home screen
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
#override
Widget build(BuildContext context) {
return Bar();
}
}
I know(I thought I know) how to pass data from screen to screen but modal and widget between really let me down. Please can anyone help me with this? I really apologise for this question but I can not find an answer to this
You are close. showModalBottomSheet returns a Future. You can capture the resulting "Hello" in the following way:
class Bar extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BarItem(
name: 'Option1',
icon: Icons.category,
onPressed: () async {
String hello = await showModalBottomSheet(
isScrollControlled: true,
context: context,
builder: (context) => ToolModal(),
);
},
);
}
}
You could use a callback function to pass the data back to your home screen:
class Bar extends StatelessWidget {
final Function(String) onHello;
const Bar({Key key, this.onHello}): super(key: key);
#override
Widget build(BuildContext context) {
return BarItem(
name: 'Option1',
icon: Icons.category,
onPressed: () async {
String hello = await showModalBottomSheet(
isScrollControlled: true,
context: context,
builder: (context) => ToolModal(),
);
onHello(hello);
},
);
}
}
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
#override
Widget build(BuildContext context) {
return Bar(onHello: (String hello) {
print(hello);
});
}
}