This is a flutter app using the stepper widget to display other widgets. I need to put two checkbox tile widgets in a row and which will be seperated by columns. I have initialized the checkbox tile widget but it keeps displaying a 'only static members can be accessed in initializers' for the onChanged and value parameters
class MyHome extends StatefulWidget {
#override
MyHomeState createState() => new MyHomeState();
}
class MyHomeState extends State<MyHome> {
void onChanged(bool value){
setState(() {
_isChecked = value;
});
}
static bool _isChecked = false;
// init the step to 0th position
int current_step = 0;
List<Step> my_steps = [
new Step(
// Title of the Step
title: new Text("Residential Data"),
// Content, it can be any widget here. Using basic Text for this example
content: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text("Activity"),
),
Row(
children: <Widget>[
Column(
children: <Widget>[
CheckboxListTile(value: _isChecked, onChanged: (bool value){setState(() {
_isChecked = value;
});})
],
),
Column(
children: <Widget>[
CheckboxListTile(value: _isChecked, onChanged: (bool value){onChanged(value);},)
],
)
],
),
Row(
children: <Widget>[
Column(
children: <Widget>[
CheckboxListTile(value: _isChecked, onChanged: (bool value){onChanged(value);})
],
),
Column(
children: <Widget>[
CheckboxListTile(value: _isChecked, onChanged: (bool value){onChanged(value);},)
],
)
],
)
],
),//new Text("Hello!"),
isActive: true),
new Step(
title: new Text("Step 2"),
content: new Text("World!"),
// You can change the style of the step icon i.e number, editing, etc.
state: StepState.editing,
isActive: true),
new Step(
title: new Text("Step 3"),
content: new Text("Hello World!"),
isActive: true),
];
#override
Widget build(BuildContext context) {
return new Scaffold(
// Appbar
appBar: new AppBar(
// Title
title: new Text("Simple Material App"),
),
// Body
body: new Container(
child: new Stepper(
// Using a variable here for handling the currentStep
currentStep: this.current_step,
// List the steps you would like to have
steps: my_steps,
// Define the type of Stepper style
// StepperType.horizontal : Horizontal Style
// StepperType.vertical : Vertical Style
type: StepperType.vertical,
// Know the step that is tapped
onStepTapped: (step) {
// On hitting step itself, change the state and jump to that step
setState(() {
// update the variable handling the current step value
// jump to the tapped step
current_step = step;
});
// Log function call
print("onStepTapped : " + step.toString());
},
onStepCancel: () {
// On hitting cancel button, change the state
setState(() {
// update the variable handling the current step value
// going back one step i.e subtracting 1, until its 0
if (current_step > 0) {
current_step = current_step - 1;
} else {
current_step = 0;
}
});
// Log function call
print("onStepCancel : " + current_step.toString());
},
// On hitting continue button, change the state
onStepContinue: () {
setState(() {
// update the variable handling the current step value
// going back one step i.e adding 1, until its the length of the step
if (current_step < my_steps.length - 1) {
current_step = current_step + 1;
} else {
current_step = 0;
}
});
// Log function call
print("onStepContinue : " + current_step.toString());
},
)),
);
}
}
You created your widget tree as a field of the class:
class Foo extends StatelessWidget {
String parameter;
Widget widget = Text(parameter); // only static members can be accessed in initializers
}
You shouldn't do this. You cannot initialize a field of an object with other class properties. Instead, create that widget inside the build method:
class Foo extends StatelessWidget {
String parameter;
#override
Widget build(BuildContext context) {
Widget widget = Text(parameter);
// TODO: do something width `widget`
}
}
Related
So I'm relatively new to flutter and I've been trying to dynamically add Sections(TextFormFields) that are represented in a form that has Form.Helper as its child and in the process to get the saveAndValidate method to work i had to use a GlobalKey to be able to access the currentState of its so i can validate and save user input and such, but whenever i try add another Section to the screen it display this error massage
════════ Exception caught by widgets library ═══════════════════════════════════
Multiple widgets used the same GlobalKey.
════════════════════════════════════════════════════════════════════════════════
here is the code I wrote and I'd appreciate any help in solving this error please.
#1- the code for the model I used:
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
class AddCourse with ChangeNotifier {
String? sectionName;
List<String>? sections;
List<dynamic>? addVids;
AddCourse({this.sectionName, this.sections, this.addVids});
/*where we save our values later to push them to firbase/database*/
Map<String, dynamic> toJson() {
final Map<String, dynamic> sectionData = <String, dynamic>{};
sectionData['Section #'] =
sections; // where current section number is saved and is stored dynamicly and updates as user adds more or less sections.
sectionData['Section Name'] =
sectionName; // where the input of the textformfield is saved and to be later pushed to the database and also is stored in a list so it can hold multiple section names as such.
return sectionData;
}
/* this is another model data for a functionality thats not implemented yet*/
Map<dynamic, dynamic> toJson2() {
final Map<dynamic, dynamic> vidData = <dynamic, dynamic>{};
vidData['Videos #'] = addVids;
return vidData;
}
}
#2 this the code for the form I created
import 'package:flutter/material.dart';
import 'package:snippet_coder_utils/FormHelper.dart';
import '../provider/course_add_model.dart';
class CourseCardBody extends StatefulWidget {
const CourseCardBody({
Key? key,
}) : super(key: key);
#override
State<CourseCardBody> createState() => _CourseCardBodyState();
}
class _CourseCardBodyState extends State<CourseCardBody> {
/* this is where i set up my global key that has the type of GlobalKey<FormState>*/
/*State associated with a [Form] widget. such as textformfields/forms/textfields..etc// the use of the (FormState) is to be able to Access the Functions "save"/"validate"/"reset" as to use them with forms/textformfields that you want to validate thier input or save it*/
GlobalKey<FormState> globalkey = GlobalKey();
AddCourse coursesModel = AddCourse();
#override
void initState() {
super.initState();
coursesModel.sections = List<String>.empty(growable: true);
coursesModel.sections?.add("");
// adds empty sections to the list of sections when the add button is used
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Add Courses'),
centerTitle: true,
),
body: ListView.separated(
shrinkWrap: true,
physics: const ScrollPhysics(),
itemBuilder: ((context, index) => Column(
children: [
_uiWidget(index),
Center(
// the submit button here needs some work to only be show once but for now sorry for this annoying button.
child: FormHelper.submitButton('Save', () {
if (validateAndSave()) {
print(coursesModel.toJson());
}
}),
),
],
)),
separatorBuilder: ((context, index) => const Divider()),
itemCount: coursesModel.sections!.length,
),
);
}
Widget _uiWidget(index) {
/* this form here is the parent of form fields/Formhelper widgets as seen below*/
return Form(
/* -- note here--
if we use a UniqueKey()
instead of our globalkey
here and comment the ValidateAndSave() function here
the form will work in terms of adding and removing sections
but we won't be able to either
save content/input of the user in the fields or
either validate
them so that sucks. */
/*this form is where global key is first used*/
key: globalkey,
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_sectionsContainer(index),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Flexible(
flex: 1,
fit: FlexFit.loose,
child: FormHelper.inputFieldWidgetWithLabel(
context,
'Add Section$index',
'',
'Section Title',
(onValidate) {
if (onValidate.isEmpty) {
return 'section ${index + 1} name cant be empty';
}
return null;
},
(onSavedVal) {
coursesModel.sections![index++] = index.toString();
onSavedVal = index;
},
onChange: (onChangedval) {
coursesModel.sectionName = onChangedval;
},
initialValue: coursesModel.sectionName ?? "",
borderColor: Colors.black,
borderFocusColor: Colors.black,
fontSize: 14,
labelFontSize: 14,
validationColor: Colors.redAccent,
),
),
Visibility(
visible: index == coursesModel.sections!.length - 1,
child: IconButton(
onPressed: () {
addEmailControl();
},
icon: const Icon(
Icons.add_circle,
color: Colors.greenAccent,
),
),
),
Visibility(
visible: index > 0,
child: SizedBox(
width: 35,
child: IconButton(
onPressed: () {
removeEmailControl(index);
},
icon: const Icon(
Icons.remove_circle,
color: Colors.redAccent,
),
),
),
),
],
),
],
),
),
);
}
Widget _sectionsContainer(index) {
/* the widget used to create the current section displayed on the top left of each textformfields*/
return Column(
children: [
Padding(
padding: const EdgeInsets.all(10),
child: Text(
'Section ${index + 1}',
textAlign: TextAlign.left,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
),
),
),
],
);
}
void addEmailControl() {
setState(() {
coursesModel.sections!.add('');
});
}
void removeEmailControl(index) {
setState(() {
if (coursesModel.sections!.length > 1) {
coursesModel.sections!.removeAt(index);
}
});
}
bool validateAndSave() {
/* we're especially using the <FormState> that is provided by the Globalkey to be able access the currentState of widget/form that has the global key in order to either validate or save the textformfields input or both in the same time*/
// validate each form
if (globalkey.currentState!.validate()) {
// If all data are correct then save data to out variables
// save each form
globalkey.currentState!.save();
return true;
} else {
return false;
}
}
}
I'm trying my best to figure it out on my own as I want to know how to solve this problem properly and where did I go wrong, and any help is very much appreciated thank you!
I suggest to create List<GlobalKey> variable. When you dynamically add or delete sub forms, you add or remove list items accordingly. It is impossible to use same GlobalKey for multiple widgets. So you need to create separate GlobalKeys for each form.
You may create a file of Global variables that may be shared across multiple files to ensure you are using a single instance.
Example globals.dart file
GlobalKey<SomeState> myGlobalKey = GlobalKey<SomeState>();
Example of implementation inside main.dart (or whatever file)
import './[path-to-globals]/globals.dart' // enter the appropriate path for your project
... // some code
Form(
key: myGlobalKey,
... // code
)
... // maybe more code
i'm trying to supply an initial value however i keep getting errors. how do i approach this. stuff is a list of values by the way so it holds a list<map<string, dynamic>> function
Widget _buildDescriptionTextField(){
return Padding(
padding: const EdgeInsets.only(right: 30),
child: TextFormField(
decoration: InputDecoration(labelText: 'Description',),
initialValue: widget.stuff['description'],
maxLines: 4,
validator: (String? value){
//if (value!.length <= 0){ // use one or the other
if (value!.isEmpty || value.length < 10 ){ // (||) means or
return 'Description is required and should be 10+ characters';
}
},
onSaved: (String? value){
setState(() {
formData['description'] = value!;
// descriptionValue = value!;
});
},),
);
}
Here is the example for initial text in text field
class _FooState extends State<Foo> {
TextEditingController _controller;
#override
void initState() {
super.initState();
_controller = new TextEditingController(text: 'Initial value');
}
#override
Widget build(BuildContext context) {
return new Column(
children: <Widget>[
new TextField(
// The TextField is first built, the controller has some initial text,
// which the TextField shows. As the user edits, the text property of
// the controller is updated.
controller: _controller,
),
new RaisedButton(
onPressed: () {
// You can also use the controller to manipuate what is shown in the
// text field. For example, the clear() method removes all the text
// from the text field.
_controller.clear();
},
child: new Text('CLEAR'),
),
],
);
}
}
How can I display Image A on the user's screen if it is false or Image B if it is true, Image A is the first one that appears, when the user clicks on it, the state changes to true and switches to Image B, and switches once the user clicks on it, the state changes to true or false.
Image A = false
Image B = true
Image A - Image B
class _MyAppState extends State<MyApp> {
bool closedImage = false;
bool openImage = true;
bool switchOn = false;
void _onSwitchChanged(bool value) {
setState(() {
switchOn = false;
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(scaffoldBackgroundColor: Colors.white),
home: Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
elevation: 0,
),
body:
Center(
child: InkWell(
onTap: () {
Switch(
onChanged: _onSwitchChanged,
value: switchOn,
);
},
child: Container(
color: Colors.white,
child: ClipRRect(
child: switchOn ? Image.asset('lib/assets/closed.png') : Image.asset('lib/assets/open.png')
)
),
),
)
),
);
}
}
Just toggle the switchOn variable like this:
void _onSwitchChanged(bool value) {
setState(() {
switchOn = !switchOn;
});
}
I think your method _onSwitchChanged needs to use the incoming bool value argument (which is supplied by the Switch).
Here's a similar example showing typical usage:
import 'package:flutter/material.dart';
class SwitchFieldPage extends StatefulWidget {
#override
_SwitchFieldPageState createState() => _SwitchFieldPageState();
}
class _SwitchFieldPageState extends State<SwitchFieldPage> {
bool switchVal = false;
String monkey = 'A';
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Switch Field'),
),
body: SafeArea(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text('Monkey $monkey'),
Switch(
onChanged: (val) { // ← remember to use val (bool)
print('Switch value: $val');
setState(() {
switchVal = val; // this sets the Switch setting on/off
monkey = val ? 'B' : 'A'; // change your monkey source
});
},
value: switchVal,
)
],
),
),
),
);
}
}
You can use a GestureDetector or InkWell to detect when the user presses on the image. For updating the image, I'd suggest learning state management. To make this simple for now, we're going to use StreamBuilder.
screen.dart:
final ScreenBloc _screenBloc = ScreenBloc();
// This is inside your widget build
StreamBuilder<AuthState>(
stream: _screenBloc.pic,
initialData: false,
builder: (context, snapshot) {
return GestureDetector(
onTap: ()=> _screenBloc.toggle(),
child: snapshot.data?Image.asset('lib/assets/closed.png') : Image.asset('lib/assets/open.png'),
);
},
)
screen_bloc.dart:
class ScreenBloc{
bool _currentState=false;
StreamController<bool> _picStream = StreamController<bool>();
Stream<bool> get pic => _picStream.stream;
void toggle(){
_currentState=!_currentState;
_picStream.add(_currentState);
}
}
I'm trying to change my icon after I tap on my List Item. I already tried different things: I tried the onTap method but the icon just does not want to change. I'm very new to flutter and I would love to find some help for my problem :). Here is my code.
I already searched for solutions but I didn't got it working in my project
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'To-Do List',
theme: ThemeData(
primaryColor: Colors.white,
brightness: Brightness.dark,
),
home: Scaffold(
appBar: AppBar(title: Text('To-Do List'),
backgroundColor: Colors.amber,
),
body: BodyLayout(),
),
);
}
}
class BodyLayout extends StatefulWidget {
#override
BodyLayoutState createState() {
return new BodyLayoutState();
}
}
class BodyLayoutState extends State<BodyLayout> {
// The GlobalKey keeps track of the visible state of the list items
// while they are being animated.
final GlobalKey<AnimatedListState> _listKey = GlobalKey();
// backing data
List<String> _data = [];
final _isdone = Set<String>();
// bool selected = false;
List<bool> selected = new List<bool>();
Icon notdone = Icon(Icons.check_box_outline_blank);
Icon done = Icon(Icons.check_box);
TextEditingController todoController = TextEditingController();
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
SizedBox(
height: 445,
child: AnimatedList(
// Give the Animated list the global key
key: _listKey,
initialItemCount: _data.length,
// Similar to ListView itemBuilder, but AnimatedList has
// an additional animation parameter.
itemBuilder: (context, index, animation) {
// Breaking the row widget out as a method so that we can
// share it with the _removeSingleItem() method.
return _buildItem(_data[index], animation);
},
),
),
TextField(
controller: todoController,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'To-Do'
),
),
RaisedButton(
child: Text('Insert item', style: TextStyle(fontSize: 20)),
onPressed: () {
_insertSingleItem();
},
),
RaisedButton(
child: Text('Remove item', style: TextStyle(fontSize: 20)),
onPressed: () {
_removeSingleItem();
},
)
],
);
}
// This is the animated row with the Card.
Widget _buildItem(String item, Animation animation) {
final isdone = _isdone.contains(item);
selected.add(false);
return SizeTransition(
sizeFactor: animation,
child: Card(
child: ListTile(
title: Text(
item,
style: TextStyle(fontSize: 20),
),
trailing: Icon(
isdone ? Icons.check_box: Icons.check_box_outline_blank
),
onTap: (){
setState(() {
});
},
),
),
);
}
void _insertSingleItem() {
int insertIndex = 0;
setState(() {
_data.insert(0, todoController.text);
});
// Add the item to the data list.
// Add the item visually to the AnimatedList.
_listKey.currentState.insertItem(insertIndex);
}
void _removeSingleItem() {
int removeIndex = 0;
// Remove item from data list but keep copy to give to the animation.
String removedItem = _data.removeAt(removeIndex);
// This builder is just for showing the row while it is still
// animating away. The item is already gone from the data list.
AnimatedListRemovedItemBuilder builder = (context, animation) {
return _buildItem(removedItem, animation);
};
// Remove the item visually from the AnimatedList.
_listKey.currentState.removeItem(removeIndex, builder);
}
}```
You have already mentioned the icons above. You simply need to use them instead of declaring new ones again.
// This is the animated row with the Card.
Widget _buildItem(String item, Animation animation) {
final isdone = _isdone.contains(item);
selected.add(false);
return SizeTransition(
sizeFactor: animation,
child: Card(
child: ListTile(
title: Text(
item,
style: TextStyle(fontSize: 20),
),
trailing: isdone ? done: notdone, // use the icon variables you have already defined
onTap: (){
setState(() {
// add the item to _isdone set if it is not added and remove it if it is added when tapped on the list item
if(isdone) {
_isdone.remove(item);
} else {
_isdone.add(item);
}
});
},
),
),
);
}
In this code, I have added the item and removed the item in setSate() in the onTap(), so that whenever you tap the list item, _isdone Set gets updated and the build() is reloaded. Which makes your layout and data update itself every time you tap on the list item.
I get an error while I'm building a ListView. In this flutter app I try to count for each column some points when a button is clicked. But I'm getting always the same error.
══╡ EXCEPTION CAUGHT BY GESTURE
I/flutter (28729): The following RangeError was thrown while handling
a gesture: I/flutter (28729): RangeError (index): Invalid value: Valid
value range is empty: 0
This is my code and I hope somebody is able to help me fixing the error:
import 'package:flutter/material.dart';
class Punktezaehler extends StatefulWidget{
final List<String> spieler_namen;
Punktezaehler(this.spieler_namen);
#override
State<StatefulWidget> createState() => new _Punktezaehler(this.spieler_namen);
}
class _Punktezaehler extends State<Punktezaehler>{
final List<String> spieler_namen;
_Punktezaehler(this.spieler_namen);
List<int> punkteanzahl_teamEins = [];
List<int> punkteanzahl_teamZwei = [];
int team1_hinzugezaehlt = 0;
int team2_hinzugezaehlt = 0;
#override
Widget build(BuildContext context) {
var spieler1 = spieler_namen[0].substring(0,3);
var spieler2 = spieler_namen[1].substring(0,3);
var spieler3 = spieler_namen[2].substring(0,3);
var spieler4 = spieler_namen[3].substring(0,3);
return new Scaffold(
appBar: new AppBar(
automaticallyImplyLeading: false,
title: new Text("$spieler1 & $spieler2 vs" +" $spieler3 & $spieler4"),
actions: <Widget>[
],
),
body: Container(
child: new Row(
children: <Widget>[
new Column(
children: <Widget>[
new IconButton(
icon: Icon(Icons.exposure_plus_2),
onPressed: () => punkte_hinzuzaehlen(1, 2)
)
],
),
new Padding(padding: EdgeInsets.only(left: 100.0)),
new Expanded(
child: ListView.builder(
itemCount: punkteanzahl_teamEins.length, //--> Error is thrown here
itemBuilder: (context, index){
return Text(punkteanzahl_teamEins[index].toString());
}
),
),
new Expanded(
child: ListView.builder(
itemCount: punkteanzahl_teamZwei.length, //--> Error is thrown here
itemBuilder: (context, index){
return Text(punkteanzahl_teamZwei[index].toString());
}
),
),
new Column(
children: <Widget>[
new IconButton(
icon: Icon(Icons.exposure_plus_2),
onPressed: () => punkte_hinzuzaehlen(2, 2)
)],
)
],
)
),
);
}
void punkte_hinzuzaehlen(int team, int nummer){
if (team == 1){
setState(() {
punkteanzahl_teamEins[team1_hinzugezaehlt] = nummer;
team1_hinzugezaehlt++;
});
}
else if(team == 2){
setState(() {
punkteanzahl_teamZwei[team2_hinzugezaehlt] = nummer;
team2_hinzugezaehlt++;
});
}
}
}
the problem that when you click a button you are calling this line
punkteanzahl_teamEins[team1_hinzugezaehlt]
and team1_hinzugezaehlt have an initial value of 0 but every time the user click the button this value will increase by one
so let's say your punkteanzahl_teamEins list contains 2 items in the fourth click the value team1_hinzugezaehlt will be 4 witch will cause this error . so the solution is to check whether the value is in the range or not
if (team1_hinzugezaehlt<punkteanzahl_teamEins.length){
setState(() {
punkteanzahl_teamEins[team1_hinzugezaehlt] = nummer;
team1_hinzugezaehlt++;
});
}
and do the same for the second function