When dynamically creating DropDownButton - it doesn't work - flutter

When creating a DropDownButton through a class, the dropdown list itself does not open. Tell me, what is my mistake?
The button is in the "inactive" state. That is, when I click on it, I do not have a list with data
Yes, I'm sure the data is there.
I would not create a class for the button, but I have about 10 of them, and in order not to have a lot of repetitive code, I need to use the class
I can provide button code -
class DropDownButtons extends StatelessWidget {
final dynamic valueDropDown;
final String hintDropDown;
final List<DropdownMenuItem<dynamic>>? itemsDropDown;
final void Function(dynamic)? saveState;
const DropDownButtons({
Key? key,
required this.valueDropDown,
required this.hintDropDown,
required this.itemsDropDown,
required this.saveState
}) : super (key: key);
#override
Widget build(BuildContext context) {
return Container(
width: 270,
margin: EdgeInsets.only(top: 7),
child: Align(
alignment: Alignment.center,
child: DropdownButtonFormField(
hint: Text('hintDropDown'),
value: valueDropDown,
icon: const Icon(Icons.keyboard_arrow_down),
items: itemsDropDown,
onChanged: saveState,
)
)
);
}
}
And also, the display code -
DropDownButtons(valueDropDown: LoginClass.countryValue, hintDropDown: 'Text', itemsDropDown: LoginClass.countryDropDown, saveState: saveStateCountry()),
And of course, saving the stat -
saveStateCountry() {
(dynamic newValue) {
setState(() {
LoginClass.countryValue = newValue!;
print(LoginClass.countryValue);
});
};
}
minimum reproductive code -
class CreatedApplications extends StatelessWidget {
const CreatedApplications({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
saveStateCountry() {
(dynamic newValue) {
setState(() {
LoginClass.countryValue = newValue!;
print(LoginClass.countryValue);
});
};
}
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
body: SingleChildScrollView(
padding: EdgeInsets.all(15),
child: Column(children: [
Column(
children: <Widget>[
Align(
alignment: Alignment.center,
child: FractionallySizedBox(
widthFactor: 1,
alignment: Alignment.centerLeft,
child: Text('${now}'))),
],
),
SizedBox(
height: 12,
),
// const DropDown(),
DropDownButtons(valueDropDown: LoginClass.countryValue, hintDropDown: 'Выберите страну', itemsDropDown: LoginClass.countryDropDown, saveState: saveStateCountry()),
],
),
),
]
)
)
);
}
}
class DropDownButtons extends StatelessWidget {
final dynamic valueDropDown;
final String hintDropDown;
final List<DropdownMenuItem<dynamic>>? itemsDropDown;
final Function saveState;
const DropDownButtons({
Key? key,
required this.valueDropDown,
required this.hintDropDown,
required this.itemsDropDown,
required this.saveState
}) : super (key: key);
#override
Widget build(BuildContext context) {
return Container(
width: 270,
margin: EdgeInsets.only(top: 7),
child: Align(
alignment: Alignment.center,
child: DropdownButtonFormField(
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: const BorderRadius.all(
const Radius.circular(25.0),
),
),
fillColor: Colors.lightBlueAccent.withOpacity(0.2),
filled: true
),
hint: Text(hintDropDown),
value: valueDropDown,
icon: const Icon(Icons.keyboard_arrow_down),
items: itemsDropDown,
onChanged: (dynamic newValue) {
saveState;
}
)
)
);
}
}

Related

How to implement dynamic scrolling via a navbar in flutter for a single-page web app?

I'm trying to find a way to click on an element in my navbar and have the page auto-scroll down to that section. My one-page website consists of a SingleChildScrollView with the different sections (e.g. about, services, contact us,..) as children of that scroll view. This structure is written in my HomeScreen class. The NavBar is in a different dart file and gets generated. How can I make it so that these get linked to each other. GIF: https://imgur.com/a/pukCfao
Homescreen class (gets initiated in main.dart)
class HomeScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: Column(
children: [
TopSection(),
SizedBox(height: kDefaultPadding * 2),
AboutSection(),
ServiceSection(),
TestimonialSection(),
SizedBox(height: kDefaultPadding),
ContactSection(),
SizedBox(height: 50)
],
),
),
);
}
}
Part where you can click on element in nav bar
class _MenuState extends State<Menu> {
int selectedIndex = 0;
int hoverIndex = 0;
List<String> menuItems = [
"Home",
"About",
"Services",
"Testimonials",
"Contact"
];
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.symmetric(horizontal: kDefaultPadding * 2.5),
constraints: BoxConstraints(maxWidth: 1110),
height: 100,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10),
topRight: Radius.circular(10),
),
boxShadow: [kDefaultShadow],
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: List.generate(
menuItems.length,
(index) => buildMenuItem(index),
),
),
);
}
Widget buildMenuItem(int index) => InkWell(
onTap: () {
setState(() {
selectedIndex = index;
});
},
Example of navbar with underneath the sections
UPDATE:
I solved the problem with a little change in your code and work for me :)
I use ValueNotifier and ValueLisenableBuilder for transfer value & use ValueChanged for pass value to outside the menu widget.
but better and recommended using one state manager for this work .
for switch scroll can using GlobalKey and Scrollable.ensureVisible.
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(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomeScreen(),
);
}
}
class HomeScreen extends StatefulWidget {
#override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
late ValueNotifier<int> notifier;
final List<GlobalKey> itemKeys = [
GlobalKey(),
GlobalKey(),
GlobalKey(),
GlobalKey(),
GlobalKey()
];
#override
void initState() {
notifier = ValueNotifier(0);
super.initState();
}
#override
void dispose() {
notifier.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: ValueListenableBuilder<int>(
valueListenable: notifier,
builder: (context, val, widget) {
return Column(children: [
Menu(
valueChanged: (int value) {
print("$value");
notifier.value = value;
notifier.notifyListeners();
Scrollable.ensureVisible(itemKeys[value].currentContext!,
duration: Duration(seconds: 1),
// duration for scrolling time
alignment: .5,
// 0 mean, scroll to the top, 0.5 mean, half
curve: Curves.easeInOutCubic);
},
),
Item(title: "Home", key: itemKeys[0]),
Item(title: "About", key: itemKeys[1]),
Item(title: "Services", key: itemKeys[2]),
Item(title: "Testimonials", key: itemKeys[3]),
Item(title: "Contact", key: itemKeys[4]),
]);
},
)),
);
}
}
class Item extends StatelessWidget {
const Item({Key? key, required this.title}) : super(key: key);
final String title;
#override
Widget build(BuildContext context) {
return Container(
height: 400,
width: double.infinity,
color: Colors.deepPurpleAccent,
child: Text("$title"),
);
}
}
class Menu extends StatefulWidget {
Menu({Key? key, required this.valueChanged}) : super(key: key);
ValueChanged<int> valueChanged;
#override
State<Menu> createState() => _MenuState();
}
class _MenuState extends State<Menu> {
int selectedIndex = 0;
int hoverIndex = 0;
List<String> menuItems = [
"Home",
"About",
"Services",
"Testimonials",
"Contact"
];
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 70 * 2.5),
constraints: BoxConstraints(maxWidth: 1110),
height: 100,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10),
topRight: Radius.circular(10),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: List.generate(
menuItems.length,
(index) => Container(
color: selectedIndex == index ? Colors.red : Colors.green,
width: 100,
height: 100,
child: InkWell(
child: Text("${menuItems[index]}"),
onTap: () {
setState(() {
selectedIndex = index;
widget.valueChanged((index));
});
},
),
),
),
),
);
}
}

Get two different selected values from dropdowbutton from other class _ Flutter or GetX

To organize the code in a Form, I put the "Dropdownbutton" code in a separate class, and I call this class for each dropdown list. Indeed in this case, I have two drop down lists. At the level of the onChange() function, I assign the selected value to the selectedVal variable. So my question is how to access this value and pass it through the form after clicking on the submit button. Can anyone tell me how to solve this problem using Flutter or Getx?!
Thank you in advance.
Class of Form:
class InscriptionView extends StatefulWidget {
const InscriptionView({Key? key}) : super(key: key);
#override
State<StatefulWidget> createState() {
return InscriptionViewState();
}
}
class InscriptionViewState extends State<InscriptionView> {
GlobalKey<FormState> formstate2 = GlobalKey();
String? selectedValGender;
String? selectedDiploma;
inscription() async {
print(selectedValGender);
print(selectedDiploma);
//Code
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
padding: const EdgeInsets.all(20),
child: Form(
key: formstate2,
child: Column(children: [
const CustomDropDownButton(
hint: "Gender",
listeItems: ["male", "female"],
icon: Icon(Icons.person),
),
const SizedBox(height: 20),
const CustomDropDownButton(
hint: "Diploma",
listeItems: ["Bachelor", "Master"],
icon: Icon(Icons.school_outlined),
),
const SizedBox(height: 20),
Center(
child: InkWell(
child: const Text("Page de connexion"),
onTap: () async {
await inscription();
},
))
]),
)));
}
}
Class of customise DropDownButton:
class CustomDropDownButton extends StatefulWidget {
final List<String>? listeItems;
final String? hint;
final Widget? icon;
String? selectedGender;
String? selectedDiploma;
const CustomDropDownButton(
{Key? key,
this.listeItems,
this.hint = '',
this.icon})
: super(key: key);
#override
State<CustomDropDownButton> createState() => _CustomDropDownButtonState();
}
class _CustomDropDownButtonState extends State<CustomDropDownButton> {
#override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 20),
decoration: BoxDecoration(
color: Colors.white, borderRadius: BorderRadius.circular(50)),
child: DropdownButton<String>(
dropdownColor: const Color.fromARGB(255, 192, 196, 220),
borderRadius: BorderRadius.circular(20),
isExpanded: true,
hint: Text(widget.hint!),
icon: widget.icon,
value: CustomDropDownButton.selectedGender,
items: widget.listeItems!
.map((e) => DropdownMenuItem(
value: e,
child: Text(e),
))
.toList(),
onChanged: (val) {
setState(() {
selectedGender = val!;
});
}),
);
}
}

Adding a toggled button/icon to App in flutter

Looking for a way to implement a button that can be toggled back and forth between favorites and history. Is there a way to do this in flutter.
You can try using a custom widget like this one below:
toggle_button.dart
import 'package:flutter/material.dart';
class ToggleButton extends StatefulWidget {
final double width;
final double height;
final String leftDescription;
final String rightDescription;
final Color toggleColor;
final Color toggleBackgroundColor;
final Color toggleBorderColor;
final Color inactiveTextColor;
final Color activeTextColor;
final double _leftToggleAlign = -1;
final double _rightToggleAlign = 1;
final VoidCallback onLeftToggleActive;
final VoidCallback onRightToggleActive;
const ToggleButton(
{Key? key,
required this.width,
required this.height,
required this.toggleBackgroundColor,
required this.toggleBorderColor,
required this.toggleColor,
required this.activeTextColor,
required this.inactiveTextColor,
required this.leftDescription,
required this.rightDescription,
required this.onLeftToggleActive,
required this.onRightToggleActive})
: super(key: key);
#override
_ToggleButtonState createState() => _ToggleButtonState();
}
class _ToggleButtonState extends State<ToggleButton> {
double _toggleXAlign = -1;
late Color _leftDescriptionColor;
late Color _rightDescriptionColor;
#override
void initState() {
super.initState();
_leftDescriptionColor = widget.activeTextColor;
_rightDescriptionColor = widget.inactiveTextColor;
}
#override
Widget build(BuildContext context) {
return Container(
width: widget.width,
height: widget.height,
decoration: BoxDecoration(
color: widget.toggleBackgroundColor,
borderRadius: BorderRadius.all(
Radius.circular(50.0),
),
border: Border.all(color: widget.toggleBorderColor),
),
child: Stack(
children: [
AnimatedAlign(
alignment: Alignment(_toggleXAlign, 0),
duration: Duration(milliseconds: 300),
child: Container(
width: widget.width * 0.5,
height: widget.height,
decoration: BoxDecoration(
color: widget.toggleColor,
borderRadius: BorderRadius.all(
Radius.circular(50.0),
),
),
),
),
GestureDetector(
onTap: () {
setState(
() {
_toggleXAlign = widget._rightToggleAlign;
_leftDescriptionColor = widget.inactiveTextColor;
_rightDescriptionColor = widget.activeTextColor;
},
);
widget.onRightToggleActive();
},
child: Align(
alignment: Alignment(-1, 0),
child: Container(
width: widget.width * 0.5,
color: Colors.transparent,
alignment: Alignment.center,
child: Text(
widget.leftDescription,
style: TextStyle(
color: _leftDescriptionColor,
fontWeight: FontWeight.bold),
),
),
),
),
GestureDetector(
onTap: () {
setState(
() {
_toggleXAlign = widget._leftToggleAlign;
_leftDescriptionColor = widget.activeTextColor;
_rightDescriptionColor = widget.inactiveTextColor;
},
);
widget.onLeftToggleActive();
},
child: Align(
alignment: Alignment(1, 0),
child: Container(
width: widget.width * 0.5,
color: Colors.transparent,
alignment: Alignment.center,
child: Text(
widget.rightDescription,
style: TextStyle(
color: _rightDescriptionColor,
fontWeight: FontWeight.bold),
),
),
),
),
],
),
);
}
}
main.dart
import 'package:flutter/material.dart';
import 'package:stackovfl_70777885/toggle_button.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
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> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Container(
padding: EdgeInsets.all(10.0),
child: ToggleButton(
width: 300.0,
height: 60.0,
toggleBackgroundColor: Colors.white,
toggleBorderColor: (Colors.grey[350])!,
toggleColor: (Colors.indigo[900])!,
activeTextColor: Colors.white,
inactiveTextColor: Colors.grey,
leftDescription: 'FAVORITES',
rightDescription: 'HISTORY',
onLeftToggleActive: () {
print('left toggle activated');
},
onRightToggleActive: () {
print('right toggle activated');
},
),
),
);
}
}
This should result in the following:
The onLeftToggleActive(): () {} and onRightToggleActive() {} in main are triggered depending on where the slider moves.

Failed assertion:'items == null || items.isEmpty || value == null || items.where((DropdownMenuItem<T> item) return item.value == value;}).length == 1'

class DropDown extends StatefulWidget {
const DropDown({
this.data,
this.hint,
Key key,
}) : super(key: key);
final List<String> data;
final String hint;
#override
_DropDownState createState() => _DropDownState();
}
String _chosenValue1;
class _DropDownState extends State<DropDown> {
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Container(
width: 250,
padding: const EdgeInsets.all(0.0),
child: DropdownButton<String>(
iconSize: 30,
isExpanded: true,
value: _chosenValue1,
//elevation: 5,
items: widget.data.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
hint: Text(
widget.hint,
style: TextStyle(
color: Colors.black,
fontSize: 13,
fontWeight: FontWeight.w600,
),
),
onChanged: (String value) {
setState(() {
_chosenValue1 = value;
});
},
),
),
);
}
}
DropDown(
data: [
'Non-Blanchable',
'Partial thickness skin',
'Full thickness skin loss involving damage or necrosis',
'Obscured by necrosis'
],
hint: 'Assessment',
),
DropDown(
data: [
'Indistinct, diffuse,none ',
'Distinct,outline clearly'
],
hint: 'Assessment',
),
i have been stuck on this problem for a while now, When i have the same data inside the data it works however all the dropdown would become the same, I want to be able to have different data for different dropdown , but when i do so the error is caused and i cant figure out whats wrong with it
import 'package:flutter/material.dart';
class DropDown extends StatefulWidget {
DropDown({
this.data,
this.hint,
this.initialValue,
Key? key,
}) : super(key: key);
final List<String>? data;
final String? hint;
final String? initialValue;
String chosenValue1 = "";
#override
_DropDownState createState() => _DropDownState();
}
class _DropDownState extends State<DropDown> {
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Container(
width: 250,
padding: const EdgeInsets.all(0.0),
child: DropdownButton<String>(
iconSize: 30,
isExpanded: true,
value: widget.initialValue!.isEmpty ? null : widget.initialValue!,
//elevation: 5,
items: widget.data!.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
hint: Text(
widget.hint!,
style: const TextStyle(
color: Colors.black,
fontSize: 13,
fontWeight: FontWeight.w600,
),
),
onChanged: (value) {
setState(() {
widget.chosenValue1 = value!;
});
},
),
),
);
}
}
import 'package:flutter/material.dart';
import 'dropdown.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
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> {
int _counter = 0;
void _incrementCounter() {
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
DropDown(
data: const [
'Non-Blanchable',
'Partial thickness skin',
'Full thickness skin loss involving damage or necrosis',
'Obscured by necrosis'
],
hint: 'Assessment',
initialValue: "Non-Blanchable",
),
DropDown(
data: const [
'Indistinct, diffuse,none',
'Distinct,outline clearly'
],
hint: 'Assessment',
initialValue: "",
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
Use the above code it will fix ur error
I tried running your code and, after making your data and hint required params and moving the chosenValue variable inside your _DropDownState, it works perfectly fine. Can you maybe share some steps with how to reproduce the error that you're seeing, because I see two different dropdowns with values I can select independently of each other.
As per your description of how to reproduce the error, I've tried adding navigation between two screens, but it still all works as intended.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Dropdowns(),
);
}
}
class Dropdowns extends StatelessWidget {
const Dropdowns();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: EdgeInsets.all(40),
child: Column(
children: [
Text('This is the first screen'),
DropDown(
data: [
'Non-Blanchable',
'Partial thickness skin',
'Full thickness skin loss involving damage or necrosis',
'Obscured by necrosis'
],
hint: 'Assessment',
),
DropDown(
data: ['Indistinct, diffuse,none ', 'Distinct,outline clearly'],
hint: 'Assessment',
),
ElevatedButton(
child: Text('Go to second screen'),
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => SecondScreen(),
),
);
},
),
],
),
),
);
}
}
class DropDown extends StatefulWidget {
const DropDown({
required this.data,
required this.hint,
Key? key,
}) : super(key: key);
final List<String> data;
final String hint;
#override
_DropDownState createState() => _DropDownState();
}
class _DropDownState extends State<DropDown> {
String? _chosenValue1;
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Container(
width: 250,
padding: const EdgeInsets.all(0.0),
child: DropdownButton<String>(
iconSize: 30,
isExpanded: true,
value: _chosenValue1,
//elevation: 5,
items: widget.data.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
hint: Text(
widget.hint,
style: TextStyle(
color: Colors.black,
fontSize: 13,
fontWeight: FontWeight.w600,
),
),
onChanged: (String? value) {
setState(() {
_chosenValue1 = value;
});
},
),
),
);
}
}
class SecondScreen extends StatelessWidget {
const SecondScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('SECOND SCREEN'),
),
body: Padding(
padding: EdgeInsets.all(40),
child: Column(
children: [
Text('This is the second screen'),
DropDown(
data: [
'Non-Blanchable',
'Partial thickness skin',
'Full thickness skin loss involving damage or necrosis',
'Obscured by necrosis'
],
hint: 'Assessment',
),
DropDown(
data: ['Indistinct, diffuse,none ', 'Distinct,outline clearly'],
hint: 'Assessment',
),
],
),
),
);
}
}
onChanged: (String value) {
setState(() {
_chosenValue = value;
selcat = null; use dropdown as category
_chosenValue == null
? Container()
: _chosenValue == "hi"
? _hi()
: _chosenValue == "hello"
? _hello()
: Container(),

break a form into multiple widget and interact with those widget in flutter

i have a form which i decided to break into multiple widget for code re- usability. the problem i am having i dont know how to interact with each components. for example, if the main form declare a variable, how do i access that variable in the custom textfield widget which is store in a different dart file.
below is the code i have
form dart file (main.dart)
import 'package:flutter/material.dart';
import 'package:finsec/widget/row_text_input.dart';
import 'package:finsec/widget/text_form_field.dart';
import 'package:finsec/widget/save_button.dart';
import 'package:finsec/utils/strings.dart';
import 'package:finsec/utils/dimens.dart';
import 'package:finsec/utils/colors.dart';
import 'package:finsec/widget/column_text_input.dart';
void main() {
runApp(MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Simple Interest Calculator App',
home: ThirdFragment(),
theme: ThemeData(
brightness: Brightness.dark,
primaryColor: Colors.indigo,
accentColor: Colors.indigoAccent),
));
}
class ThirdFragment extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _ThirdFragmentState();
}
}
class _ThirdFragmentState extends State<ThirdFragment> {
var _formKey = GlobalKey<FormState>();
var _currentItemSelected = '';
bool isError = false;
bool isButtonPressed = false;
#override
void initState() {
super.initState();
}
TextEditingController amountController = TextEditingController();
TextEditingController frequencyController = TextEditingController();
#override
Widget build(BuildContext context) {
TextStyle textStyle = Theme.of(context).textTheme.title;
return Scaffold(
appBar: AppBar(
title: Text('Simple Interest Calculator'),
),
body: Form(
key: _formKey,
child: SingleChildScrollView(
child: Column (children: [
Padding(
padding: EdgeInsets.only(top: 10.0, bottom: 5.0, left: 15.0, right: 15.0),
child: CustomTextField(textInputType:TextInputType.number,
textController: amountController,
errorMessage:'Enter Income Amount',
labelText:'Income Amount for testing'),
),
RowTextInput(inputName: 'Frequency:',
textInputType: TextInputType.number,
textController: frequencyController,
errorMessage: 'Choose Income Frequency',
labelText: 'Income Amount for testing'
),
RowTextInput(inputName: 'Date Paid:',
textInputType: TextInputType.number,
textController: datePaidController,
errorMessage: 'Pick Income Payment Date',
labelText: 'Income Amount for testing'
),
SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
MaterialButton(
height: margin_40dp,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(margin_5dp)),
minWidth: (MediaQuery.of(context).size.width * .9) / 2,
color: Theme.of(context).primaryColor,
textColor: white,
child: new Text(save),
onPressed: () => {
setState(() {
if (_formKey.currentState.validate()) {
// amountController.text.isEmpty ? amountController.text='Value require' : amountController.text='';
//this.displayResult = _calculateTotalReturns();
}
})
},
splashColor: blueGrey,
),
MaterialButton(
height: margin_40dp,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(margin_5dp)),
minWidth: (MediaQuery.of(context).size.width * .9) / 2,
color: Theme.of(context).primaryColor,
textColor: white,
child: new Text(save_and_continue),
onPressed: () => {},
splashColor: blueGrey,
)
])
]
),
),
}
RowTextInput is a different dart file that contains this code. RowTextInput.dart
import 'package:flutter/material.dart';
import 'package:finsec/utils/hex_color.dart';
class CustomTextField extends StatelessWidget {
CustomTextField({
this.textInputType,
this.textController ,
this.errorMessage,
this.labelText,
});
TextInputType textInputType;
TextEditingController textController;
String errorMessage, labelText;
#override
Widget build(BuildContext context) {
bool isError = false;
return Container(
child: TextFormField(
keyboardType: textInputType,
style: Theme
.of(context)
.textTheme
.title,
controller: textController,
validator: (String value) {
if (value.isEmpty) {
return errorMessage;
}
},
decoration: InputDecoration(
labelStyle: TextStyle(
color: Colors.grey,
fontSize: 16.0
),
contentPadding: EdgeInsets.fromLTRB(10.0, 10.0, 10.0, 10.0), //size of textfield
errorStyle: TextStyle(
color: Colors.red,
fontSize: 15.0
),
border: OutlineInputBorder(
borderSide: BorderSide(width:5.0),
borderRadius: BorderRadius.circular(5.0)
)
)
),
);
}
}
i want to access isError and isButtonPressed variables located in main.dart from RowTextInput.dart and be able to assign values. main.dart should then be able to see those values assign in RowTextInput.dart file.
also,i want to move the MaterialButton button in its own widget file (button.dart) but then i dont know how this dart file will interact with the main.dart file when button is click or to check values of isError and IS button pressed. basically, i am breaking the form into different components (textfield and button) and store them in their own separate file. but i want all the files main.dart, rowintputtext, button.dart(new) to be able to see values of variables in main.dart and change the values. is this possible? is there an easier way?
thanks in advance
If you think about it. In Flutter the Button and RawMaterialButton are already in other files. And the manage to do exactly what you want.
You should create a File mycustomButtons.dart.
In the file you should create a class that will build your Buttons...
But it must has two parameters in it's constructor actionSave actionSaveAndContinue.
You will then create two functions in your main something like:
void _save() {
setState(() {
if (_formKey.currentState.validate()) {
// amountController.text.isEmpty ? amountController.text='Value require' : amountController.text='';
//this.displayResult = _calculateTotalReturns();
}
})
}
Then you should pass your created functions as parameters:
MyCustomButtons(actionSave: _save, actionSaveAndContinue: _saveAndContinue)
So the button will have all needed information to update your main.dart variables.
The textField is pretty much the same. But you will need pass a validation function and a TextEditingController.
You can see the font of RawnMaterialButton, TextFormField to see how they receive (and pass) data from one class to an other.
I was also looking for breaking a form into multiple classes. This is that I did :
Form
Pass the onSaved function at the form level.
final _formKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
_CustomFormField(
onSaved: (value) => _myModelForm.field1 = value),
),
_CustomFormField2(
onSaved: (value) => _myModelForm.field2 = value),
)
),
RaisedButton(
onPressed: () {
// Validate will return true if the form is valid, or false if
// the form is invalid.
if (_formKey.currentState.validate()) {
// Process data.
_formKey.currentState.save();
// Observe if your model form is updated
print(myModelForm.field1);
print(myModelForm.field2)
}
},
child: Text('Submit'),
),
],
),
);
}
_CustomFormField1
The onSaved function will be passed as argument. This class can be either in the same file than the form or in another dedicated file.
class _CustomFormField1 extends StatelessWidget {
final FormFieldSetter<String> onSaved;
//maybe other properties...
_CustomFormField1({
#required this.onSaved,
});
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 10.0),
child: TextFormField(
// You can keep your validator here
validator: (value) {
if (value.isEmpty) {
return 'Please enter some text';
}
return null;
},
onSaved: onSaved,
),
);
}
}
Like onSaved, you can do the same way for focusNode, onFieldSubmitted, validator if needed in
I hope it will help you and others
There's probably a more elegant way to do it but I am currently experimenting with Singletons. See the code below:
import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'dart:async';
class AppModel {
TextEditingController nameController;
TextEditingController surnameController;
StreamController<String> fullnameStreamController;
AppModel() {
nameController = TextEditingController();
surnameController = TextEditingController();
fullnameStreamController = StreamController.broadcast();
}
update() {
String fullname;
if (nameController.text != null && surnameController.text != null) {
fullname = nameController.text + ' ' + surnameController.text;
} else {
fullname = 'Please enter both names';
}
fullnameStreamController.add(fullname);
}
}
GetIt getIt = new GetIt();
final appModel = getIt.get<AppModel>();
void main() {
getIt.registerSingleton<AppModel>(AppModel());
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(title: 'Singleton Demo'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String text;
update() {
setState(() {
});
}
#override
void initState() {
text = 'waiting for input';
appModel.fullnameStreamController.stream.listen((data) {
text = data;
update();
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(color: Colors.amberAccent),
child: Column(
children: <Widget> [
Card(
color: Colors.white,
child: Text('Name'),
),
Card(
color: Colors.yellow,
child: NameTextField()
),
Divider(),
Card(
color: Colors.white,
child: Text('Surname'),
),
Card(
color: Colors.yellow,
child: SurnameTextField()
),
OkButton(),
Card(
color: Colors.white,
child: Text('Full name'),
),
Card(
color: Colors.orange,
child: FullnameText(text),
),
],
),
),
);
}
}
class NameTextField extends StatefulWidget {
NameTextField({Key key}) : super(key: key);
_NameTextFieldState createState() => _NameTextFieldState();
}
class _NameTextFieldState extends State<NameTextField> {
#override
Widget build(BuildContext context) {
return Container(
child: TextField(
controller: appModel.nameController,
),
);
}
}
class SurnameTextField extends StatefulWidget {
SurnameTextField({Key key}) : super(key: key);
_SurnameTextFieldState createState() => _SurnameTextFieldState();
}
class _SurnameTextFieldState extends State<SurnameTextField> {
#override
Widget build(BuildContext context) {
return Container(
child: TextField(
controller: appModel.surnameController,
),
);
}
}
class FullnameText extends StatefulWidget {
FullnameText(this.text,{Key key}) : super(key: key);
final String text;
_FullnameTextState createState() => _FullnameTextState();
}
class _FullnameTextState extends State<FullnameText> {
#override
Widget build(BuildContext context) {
return Container(
child: Text(widget.text),
);
}
}
class OkButton extends StatefulWidget {
OkButton({Key key}) : super(key: key);
_OkButtonState createState() => _OkButtonState();
}
class _OkButtonState extends State<OkButton> {
#override
Widget build(BuildContext context) {
return Container(
color: Colors.white10,
child: RaisedButton(
color: Colors.white,
child: Icon(Icons.check),
onPressed: () {appModel.update();},
),
);
}
}
Check how I use the three controllers in the update function of the AppModel class.
CustomTextFields must extends parent(widget where is form) in this case it is ThirdFragment
class CustomTextField extends ThirdFragment{
CustomTextField({
this.textInputType,
this.textController,
this.errorMessage,
this.labelText,
});