How to validate and send the data to another page in flutter - flutter

I am a newbie to flutter
and i would to like to create simple input data and pass the data to other page and show the text.
here is my code
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
const appTitle = 'Form Validation Demo';
return MaterialApp(
title: appTitle,
home: Scaffold(
appBar: AppBar(
title: const Text(appTitle),
),
body: const MyForm(),
),
);
}
}
class MyForm extends StatefulWidget {
const MyForm({Key? key}) : super(key: key);
#override
MyFormState createState() {
return MyFormState();
}
}
class MyFormState extends State<MyForm> {
final _formKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextFormField(
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter some text';
}
return null;
},
),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Page2(_formKey)),
);
}
},
child: const Text('Submit'),
),
],
),
);
}
}
class Page2 extends StatelessWidget {
Page2(final data){
this.data = data;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Page 2'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(this.data),
RaisedButton(
child: Text('BACK'),
onPressed: () {
Navigator.pop(context);
},
),
],
),
),
);
}
}
But finally, it gets
The setter 'data' isn't defined for the type 'Page2'.
error
Any idea how to do it??
and what's wrong with my code
Thank you very much.

You can make a global variable and easily use it on other screen. all you need is updating some of your code to the following
final _formKey = GlobalKey<FormState>();
TextEditingController inputController = TextEditingController();
String result;
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
const appTitle = 'Form Validation Demo';
return MaterialApp(
title: appTitle,
home: Scaffold(
appBar: AppBar(
title: const Text(appTitle),
),
body: const MyForm(),
),
);
}
}
class MyForm extends StatefulWidget {
const MyForm({Key? key}) : super(key: key);
#override
MyFormState createState() {
return MyFormState();
}
}
class MyFormState extends State<MyForm> {
final _formKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextFormField(
controller: inputController,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter some text';
}
return null;
},
),
ElevatedButton(
onPressed: () {
if (_formKey.currentState.validate()) {
setState(() {
result = inputController.text;
});
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Page2()),
);
}
},
child: const Text('Submit'),
),
],
),
),
),
);
}
}
class Page2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Page 2'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(result),
RaisedButton(
child: Text('BACK'),
onPressed: () {
Navigator.pop(context);
},
),
],
),
),
);
}
}

You should define data in Page2, something like this :
class Page2 extends StatelessWidget {
final data = GlobalKey<FormState>();
Page2(final data){
this.data = data;
}

See Send data to a new screen in the Flutter cookbook.

Related

How can I pass data from a TextFieldInput to another Page with Provider

I want to pass the Data (String) from a TextField to a second Page with Provider after I clicked the Button.
Here is my code
Update your name_provider class
class NameProvider extends ChangeNotifier {
String _name = "";
String get getName => _name;
saveName(String name) {
_name = name;
notifyListeners();
}
}
The name variable was made private to avoid getting wrong result while calling the function.
Now this edit was made to main.dart file
class _MyHomePageState extends State<MyHomePage> {
String name = "";
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Padding(
padding: const EdgeInsets.all(20),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'Please enter your name',
),
TextField(
onSubmitted: (value) {
setState(() {
Provider.of<NameProvider>(context).saveName(value);
});
},
onChanged: (value) {
setState(() {
name = value;
});
},
),
Text("Name: $name"),
TextButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const SecondPage(),
),
);
},
child: const Text("To Second Page"),
),
],
),
),
),
);
}
}
Now getting name in SecondPage:
class SecondPage extends StatelessWidget {
const SecondPage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Second Page"),
),
body: Padding(
padding: const EdgeInsets.all(20),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Text(
"Name:",
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
Text(
Provider.of<NameProvider>(context).getName,
style: TextStyle(fontSize: 32),
),
],
),
),
),
);
}
}
If there is some sort of expected string but got... error, replace getName with '${Provider.of<NameProvider>(context).getName}'
Let us know if this solution works
First of all, in the main.dart file at line 70, do this:
Provider.of<NameProvider>(context).saveName(name);
and then on the second page where you want to use this Provider.of<NameProvider>(context).name;
Here is the full working code.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class ProviderCheck extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => NameProvider(),
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Provider Problem'),
),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String name = "";
//it will store data in typed in textfield
final TextEditingController _textEditingController = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Padding(
padding: const EdgeInsets.all(20),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'Please enter your name',
),
TextField(
controller: _textEditingController,//texteditingcontroller set to textfield
onSubmitted: (value) {
setState(() {
name = value;
});
},
onChanged: (value) {
setState(() {
name = value;
});
},
),
Text("Name: $name"),
TextButton(
onPressed: () {
//saveName method called using provider and name saved.
Provider.of<NameProvider>(context, listen: false)
.saveName(_textEditingController.text);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const SecondPage(),
),
);
},
child: const Text("To Second Page"),
),
],
),
),
),
);
}
}
class NameProvider extends ChangeNotifier {
String name = "";
String get getName => name;
//name1 value will set to name variable
saveName(String name1) {
name = name1;
notifyListeners();
}
}
class SecondPage extends StatelessWidget {
const SecondPage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Second Page"),
),
body: Padding(
padding: const EdgeInsets.all(20),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
//using provider, we can access the saved name
Text(
"Name:${Provider.of<NameProvider>(context, listen: false).getName}",
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
Text(
//TODO: Name should be displayed here
"Name",
style: TextStyle(fontSize: 32),
),
],
),
),
),
);
}
}
if you prefer to use provider for that purpose,
edit your provider saveName Function to :
saveName(String _name) {
name = _name;
notifyListeners();
}
in SecondPage() add
your provider to the build method like this :
class SecondPage extends StatelessWidget {
const SecondPage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
final nameProvider = Provider.of< NameProvider>(context);
then get your string value by calling :
Text(
"Name: ${nameProvider.name}",
style : ....

How to show selected checkbox on prev screen?

I need to display checkboxes selected by the user on the previous page using pop()
I have a function that displays the user's message on the previous page and I need to pass the selected checkboxes in the same way. How to pass them as arguments to pop()?
Screen with checkboxes:
const TextScreen({Key? key}) : super(key: key);
#override
State<TextScreen> createState() => _TextScreenState();
}
class _TextScreenState extends State<TextScreen> {
// initial values for checkboxes
bool _privacy = false;
bool _termsOfUse = false;
// text controller for message input
TextEditingController textController = TextEditingController();
#override
void dispose() {
textController.dispose();
super.dispose();
}
// go to result screen
void getResult(BuildContext context) {
String valueResult = textController.text;
Navigator.pop(context, valueResult);
}
#override
Widget build(BuildContext context) {
//change state for privacy checkbox
_onPrivacyChange(value) {
setState(() {
_privacy = value!;
});
}
//change state for terms of use checkbox
_onTermsOfUSeChange(value) {
setState(() {
_termsOfUse = value!;
});
}
return Scaffold(
appBar: AppBar(
title: const Text('Enter data'),
),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextField(
controller: textController,
decoration: const InputDecoration(labelText: 'Message')),
const SizedBox(height: 20),
CheckboxListTile(
title: const Text('Privacy'),
controlAffinity: ListTileControlAffinity.leading,
value: _privacy,
onChanged: _onPrivacyChange,
contentPadding: EdgeInsets.zero,
),
CheckboxListTile(
title: const Text('Terms of use'),
controlAffinity: ListTileControlAffinity.leading,
value: _termsOfUse,
onChanged: _onTermsOfUSeChange,
contentPadding: EdgeInsets.zero,
),
ElevatedButton(
onPressed: () {
getResult(context);
},
child: const Text('Display result'))
],
)),
);
}
}
Screen with results display:
class ResultScreen extends StatefulWidget {
const ResultScreen({Key? key}) : super(key: key);
#override
State<ResultScreen> createState() => _ResultScreenState();
}
class _ResultScreenState extends State<ResultScreen> {
String? _valueText = '';
#override
Widget build(BuildContext context) {
// navigation to next screen
void _navToNextScreen(BuildContext context) async {
final result = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => const TextScreen()),
);
// update widget after result comes back
setState(() {
_valueText = result;
});
}
return Scaffold(
appBar: AppBar(
title: const Text('Results'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
_navToNextScreen(context);
},
child: const Text('Enter data'),
),
const SizedBox(height: 50),
Text('Message: $_valueText'),
const SizedBox(height: 20),
Text('Checkboxes: '),
],
)),
);
}
}
I think this should be the job of a simple state management strategy; for communication between separate widgets (in this case, two page widgets), that's the cleanest approach. You should create a common service to which both page widgets are subscribed: one to trigger the changes, the other to capture them and display them, using a ChangeNotifier service along with Consumer widgets, as shown below:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(
ChangeNotifierProvider(
create: (_) => SelectedData(),
child: MyApp()
)
);
}
class SelectedData extends ChangeNotifier {
bool _privacy = false;
bool _termsOfUse = false;
String _valueResult = '';
bool get privacy => _privacy;
bool get termsOfUse => _termsOfUse;
String get valueResult => _valueResult;
set privacy(bool value) {
_privacy = value;
notifyListeners();
}
set termsOfUse(bool value) {
_termsOfUse = value;
notifyListeners();
}
set valueResult(String value) {
_valueResult = value;
notifyListeners();
}
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: ResultScreen(),
),
),
);
}
}
class TextScreen extends StatefulWidget {
const TextScreen({Key? key}) : super(key: key);
#override
State<TextScreen> createState() => _TextScreenState();
}
class _TextScreenState extends State<TextScreen> {
// text controller for message input
TextEditingController textController = TextEditingController();
#override
void initState() {
super.initState();
}
#override
void dispose() {
textController.dispose();
super.dispose();
}
// go to result screen
void getResult(BuildContext context) {
Navigator.pop(context);
}
#override
Widget build(BuildContext context) {
SelectedData data = Provider.of<SelectedData>(context, listen: false);
textController.text = data.valueResult;
//change state for privacy checkbox
_onPrivacyChange(value) {
data.privacy = value;
}
//change state for terms of use checkbox
_onTermsOfUSeChange(value) {
data.termsOfUse = value;
}
return Scaffold(
appBar: AppBar(
title: const Text('Enter data'),
),
body: Consumer<SelectedData>(
builder: (context, selectedData, child) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextField(
controller: textController,
onChanged: (value) {
data.valueResult = value;
},
decoration: const InputDecoration(labelText: 'Message')),
const SizedBox(height: 20),
CheckboxListTile(
title: const Text('Privacy'),
controlAffinity: ListTileControlAffinity.leading,
value: selectedData.privacy,
onChanged: _onPrivacyChange,
contentPadding: EdgeInsets.zero,
),
CheckboxListTile(
title: const Text('Terms of use'),
controlAffinity: ListTileControlAffinity.leading,
value: selectedData.termsOfUse,
onChanged: _onTermsOfUSeChange,
contentPadding: EdgeInsets.zero,
),
ElevatedButton(
onPressed: () {
getResult(context);
},
child: const Text('Display result'))
],
));
}
),
);
}
}
class ResultScreen extends StatefulWidget {
const ResultScreen({Key? key}) : super(key: key);
#override
State<ResultScreen> createState() => _ResultScreenState();
}
class _ResultScreenState extends State<ResultScreen> {
#override
Widget build(BuildContext context) {
// navigation to next screen
void _navToNextScreen(BuildContext context) async {
await Navigator.push(
context,
MaterialPageRoute(builder: (context) => const TextScreen()),
);
}
return Scaffold(
appBar: AppBar(
title: const Text('Results'),
),
body: Consumer<SelectedData>(
builder: (context, selectedData, child) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
_navToNextScreen(context);
},
child: const Text('Enter data'),
),
const SizedBox(height: 50),
Text('Message: ${selectedData.valueResult}'),
const SizedBox(height: 20),
const Text('Checkboxes: '),
Text('Privacy: ${selectedData.privacy}'),
Text('Terms of Use: ${selectedData.termsOfUse}')
],
));
}
),
);
}
}
Here's the output when you implement it this way:
So from what i see is you are only passing one value that is message and you what many values to pass at a time so here the map can be used and as pop() function takes dynamic returns you can pass any thing.
From your example i have created a sample example that will be a working proof which will demostrate the using map for passing data to previous screen.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: ResultScreen(),
);
}
}
class TextScreen extends StatefulWidget {
const TextScreen({Key? key}) : super(key: key);
#override
_TextScreenState createState() => _TextScreenState();
}
class _TextScreenState extends State<TextScreen> {
// initial values for checkboxes
bool _privacy = false;
bool _termsOfUse = false;
// text controller for message input
TextEditingController textController = TextEditingController();
#override
void dispose() {
textController.dispose();
super.dispose();
}
// go to result screen
void getResult(BuildContext context) {
String valueResult = textController.text;
final data = {
"message":valueResult,
"privacy": _privacy,
'terms':_termsOfUse,
};
Navigator.pop(context, data);
}
#override
Widget build(BuildContext context) {
//change state for privacy checkbox
_onPrivacyChange(value) {
setState(() {
_privacy = value!;
});
}
//change state for terms of use checkbox
_onTermsOfUSeChange(value) {
setState(() {
_termsOfUse = value!;
});
}
return Scaffold(
appBar: AppBar(
title: const Text('Enter data'),
),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextField(
controller: textController,
decoration: const InputDecoration(labelText: 'Message')),
const SizedBox(height: 20),
CheckboxListTile(
title: const Text('Privacy'),
controlAffinity: ListTileControlAffinity.leading,
value: _privacy,
onChanged: _onPrivacyChange,
contentPadding: EdgeInsets.zero,
),
CheckboxListTile(
title: const Text('Terms of use'),
controlAffinity: ListTileControlAffinity.leading,
value: _termsOfUse,
onChanged: _onTermsOfUSeChange,
contentPadding: EdgeInsets.zero,
),
ElevatedButton(
onPressed: () {
getResult(context);
},
child: const Text('Display result'))
],
)),
);
}
}
class ResultScreen extends StatefulWidget {
const ResultScreen({Key? key}) : super(key: key);
#override
State<ResultScreen> createState() => _ResultScreenState();
}
class _ResultScreenState extends State<ResultScreen> {
String? _valueText = '';
bool _privacyValue =false;
bool _termsOfUse = false;
#override
Widget build(BuildContext context) {
// navigation to next screen
void _navToNextScreen(BuildContext context) async {
final result = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => const TextScreen()),
);
if(result !=null)
{
setState(() {
if(result['message']!=null )_valueText = result['message'];
if(result['privacy']!=null) _privacyValue = result['privacy'];
if(result['terms']!=null) _termsOfUse = result['terms'];
});
}
}
return Scaffold(
appBar: AppBar(
title: const Text('Results'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
_navToNextScreen(context);
},
child: const Text('Enter data'),
),
const SizedBox(height: 50),
Text('Message: $_valueText'),
const SizedBox(height: 20),
Text('Privacy Value: $_privacyValue '),
const SizedBox(height: 20),
Text('Terms Value: $_termsOfUse '),
],
)),
);
}
}
You can make changes as per your needs, So let me know if it works.

Unable to navigate from GetX Dialog to another screen

I have follow dialog box. When I click 'Next' I want it to navigate to GamePage() screen. But unfortunately it doesn't work.
Following is the GamePage Widget
class GamePage extends StatelessWidget {
final homeCtrl = Get.find<HomeController>();
GamePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFF8fb1ca),
body: SafeArea(
child: ListView(
children: [
Padding(
padding: EdgeInsets.all(3.0.wp),
child: Row(
children: [
IconButton(
onPressed: () {
Get.back();
},
icon: const Icon(Icons.arrow_back),
),
],
),
),
Padding(
padding: EdgeInsets.symmetric(vertical: 4.0.wp),
child: Column(
children: [
SizedBox(
height: 2.0.wp,
),
Center(
child: Text(
'What ${homeCtrl.currentWord.first.wordtype} is this?',
style: TextStyle(
fontSize: 18.0.sp,
color: Colors.grey[800],
),
),
),
SizedBox(height: 10.0.wp),
WordsWidget(currentWord: homeCtrl.currentWord.first),
],
),
),
],
),
),
);
}
}
Following is the Word Widget being called from GamePage Widget
class WordsWidget extends StatelessWidget {
final currentWord;
WordsWidget({Key? key, this.currentWord}) : super(key: key);
final homeCtrl = Get.find<HomeController>();
#override
Widget build(BuildContext context) {
// var currentWord = homeCtrl.nextWord();
var shuffleword = [].obs;
shuffleword.addAll(homeCtrl.shuffleWord(currentWord.word));
TextToSpeech tts = TextToSpeech();
String language = 'en-US';
tts.setLanguage(language);
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
print('pressed here');
Get.defaultDialog(
title: 'Go to next page',
content: Container(
child: Column(
children: [
Text('You are about to move to another screen'),
ElevatedButton.icon(
onPressed: () {
Get.to(() => GamePage());
},
icon: Icon(
Icons.arrow_right,
),
label: Text('Go'))
],
),
));
},
child: Text('Open Dialog')),
],
);
}
}
Get.back() is working but not Get.to
Try
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) {
return const GamePage();
},
),
);
},
child: Text("Next Word"),
)
Try this code -
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:image_memory/next_page.dart';
import 'package:image_picker/image_picker.dart';
void main() {
//check getMaterialApp is used
runApp(const GetMaterialApp(
title: 'Temp',
home: const MyApp(),
));
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Image Picker'),
),
body: Center(
child: ElevatedButton(
onPressed: () {
print('pressed here');
Get.defaultDialog(
title: 'Go to next page',
content: Container(
child: Column(
children: [
Text('You are about to move to another screen'),
ElevatedButton.icon(
onPressed: () {
Get.to(() => NextPage());
},
icon: Icon(
Icons.arrow_right,
),
label: Text('Go'))
],
),
));
},
child: Text('Open Dialog')),
),
);
}
}
and next page is -
import 'package:flutter/material.dart';
class NextPage extends StatefulWidget {
const NextPage({ Key? key }) : super(key: key);
#override
State<NextPage> createState() => _NextPageState();
}
class _NextPageState extends State<NextPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Next Page'),
),
body: Container(
child: Center(
child: Text("this is next page"),
),
),
);
}
}
And yes, you need to insure that you are using 'GetMaterialApp'.
If you want to use GetX navigation system, you should wrap your application in a GetMaterialApp instead of MaterialApp.
So in your main use this:
class GetxApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return GetMaterialApp(
home: HomePage(),
);
}
}
Instead of this:
class NormalApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage(),
);
}
}

flutter dependOnInheritedWidgetOfExactType() returns null

I am trying to use InheritedWidget approach to share state down the Widget tree. For this, I am making a simple counter app. You can add, subtract, multiply or divide the counter however you like.
It's a small demo so best practices are not followed. The line with code context.dependOnInheritedWidgetOfExactType<MyInheritedWidget>() seem to be null for some reason. When looking at samples and doc, it should find the MyInheritedWidget in the widget tree and return it. However, I am getting complaints from flutter tool that it is null. And, in deed it is null when asserted as well.
What is the reasoning here for failed return here? And, how do I need to do it such that I can receive the instance?
File: main.dart
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Counter(),
);
}
}
class Counter extends StatefulWidget {
const Counter({Key? key}) : super(key: key);
#override
CounterState createState() => CounterState();
}
class CounterState extends State<Counter> {
int counter = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Counter App'),
centerTitle: true,
),
body: MyInheritedWidget(
counterState: this,
child: Builder(
builder: (BuildContext innerContext) {
return CounterViewer(
counterState: MyInheritedWidget.of(context).counterState);
},
),
),
);
}
void addCounter(int value) {
setState(() {
counter++;
});
}
void subtractCounter(int value) {
setState(() {
counter--;
});
}
void multiplyCounter(int value) {
setState(() {
counter *= value;
});
}
void divideCounter(int value) {
setState(() {
counter = (counter / value).toInt();
});
}
}
class MyInheritedWidget extends InheritedWidget {
final CounterState counterState;
const MyInheritedWidget(
{Key? key, required Widget child, required this.counterState})
: super(key: key, child: child);
static MyInheritedWidget of(BuildContext context) {
final MyInheritedWidget? widget =
context.dependOnInheritedWidgetOfExactType<MyInheritedWidget>();
assert(widget != null);
return widget!;
}
#override
bool updateShouldNotify(covariant InheritedWidget oldWidget) {
return true;
}
}
class CounterViewer extends StatelessWidget {
final CounterState counterState;
const CounterViewer({Key? key, required this.counterState}) : super(key: key);
#override
Widget build(BuildContext context) {
return Column(
children: [
Container(
color: Colors.green.shade200,
width: MediaQuery.of(context).size.width,
height: 180,
child: Center(
child: Text(
'220',
style: TextStyle(
color: Colors.grey.shade50,
fontSize: 60,
fontWeight: FontWeight.bold,
),
),
),
),
Container(
color: Colors.grey.shade300,
padding: EdgeInsets.symmetric(vertical: 16),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: () {},
child: Text('Add'),
),
ElevatedButton(
onPressed: () {},
child: Text('Subtract'),
),
ElevatedButton(
onPressed: () {},
child: Text('Multiply'),
),
ElevatedButton(
onPressed: () {},
child: Text('Divide'),
),
],
),
)
],
);
}
}
Update: I seem to have passed the wrong context to the dependOnInheritedWidgetOfExactType() method. Changing from context to innerContext fixed the issue.
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Counter App'),
centerTitle: true,
),
body: MyInheritedWidget(
counterState: this,
child: Builder(
builder: (BuildContext innerContext) {
return CounterViewer(
counterState: MyInheritedWidget.of(innerContext).counterState);
},
),
),
);
}

How to pass data to a widget inside of another widget

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);
}