I made a list of widgets and i want to access setter in the class DayButton, but when i try days[index]. it doesn't show the setter methods, it treats the output as a general widget but not the DayButton widget
here is the code.
class DayButtonRow extends StatefulWidget{
#override
DBRState createState() => DBRState();
}
class DBRState extends State<DayButtonRow>{
currentDay(int i){
setState(() {
current = days.elementAt(i).key;
//I want to access the setter here to change the color of the button
});
}
#override
Widget build(BuildContext context) {
currentDay(3);
return Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: days
),
);
}
}
Key current;
List<Widget> days = [
DayButton("MON"),
DayButton("TUE"),
DayButton("WED"),
DayButton("THU"),
DayButton("FRI"),
DayButton("SAT"),
DayButton("SUN"),
];
is there a way to access the DayButton widget class from the list?
As mentioned in the comments, declare it as List<DayButton>:
List<DayButton> days = [
DayButton("MON"),
DayButton("TUE"),
DayButton("WED"),
DayButton("THU"),
DayButton("FRI"),
DayButton("SAT"),
DayButton("SUN"),
];
Related
I want to have a textinputfield for the user and when he tips in something and clicks on a button the input should be shown in a list item. The user should also have the option to delete items of the list just like a in and todo app.
Here you can find my code:
Link to Code
So I decided to write this simple program just to freshen up my skills... You can directly copy paste this code and it should work just fine.
I have used the provider package here to make this a bit more professional, as you can't always rely on setState() to update your UI when tasks are added to your list. And also because you will probably be using the provider more often in the future.
I have added comments in the below code to make it easy to understand. However, do not hesitate to clear up any confusions in the comments :)
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:todo/list_provider.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider( // This initiates the provider.
create: (context) => TaskProvider(), // Initiating it here makes this provider data available everywhere in the application
child: MaterialApp(
title: 'Flutter Demo',
home: const MyHomePage(),
),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Dynamic List'),
),
body: const HomePageBody(), // I pass a separate widget here just to make the code a bit cleaner
floatingActionButton: FloatingActionButton(
onPressed: () => showModalBottomSheet( // This calls a bottom Modal Sheet which pops up while pressing the floating action button
context: context, builder: (context) => const BottomSheet()),// The modal sheet displays the BottomSheet() Widget which I have defined down in this code.
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
// This is where the ListView will be shown
class HomePageBody extends StatelessWidget {
const HomePageBody({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
List<String> listOfTasks = Provider.of<TaskProvider>(context).getTasks; // This is where the list is being accessed from the Provider file.
return Container(
padding: const EdgeInsets.all(20),
child: ListView.builder(
itemCount: listOfTasks.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(
listOfTasks[index],
),
);
},
),
);
}
}
// This is the BottomSheet Widget where I decided to take User Input from
class BottomSheet extends StatefulWidget {
const BottomSheet({Key? key}) : super(key: key);
#override
State<BottomSheet> createState() => _BottomSheetState();
}
class _BottomSheetState extends State<BottomSheet> {
String task = ''; // This variable holds the tasks user wants to add
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.only(
top: 20,
left: 20,
right: 20,
bottom: MediaQuery.of(context).viewInsets.bottom + 20, // viewInsets.bottom adds padding from the bottom to avoid keyboard overlapping textfield widget
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextFormField(// You can use TextField Widget as well
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
),
onChanged: (value) { // This saves the value in the TextField for every character the user types
task = value; // The value in the TextField is referred to by the 'value' variable
},
),
const SizedBox(
height: 10,
),
ElevatedButton(
onPressed: () => saveTask(task),
child: const Text('Save Task'),
),
],
),
);
}
void saveTask(String task) {
Provider.of<TaskProvider>(context, listen: false).addTasks(task); //This is where I am calling the function to add a task to the list.
// The 'addTasks()' function is defined in the provider file which is just below
}
}
Here's the list_provider.dart file that I import in above code:
import 'package:flutter/foundation.dart';
class TaskProvider extends ChangeNotifier { // This is the class where your data exists
// and this is the only place where your data should be manipulated! I explain the reason below...
final List<String> _tasks = [];
List<String> get getTasks { // We use a getter to retrieve the list
return _tasks; // We do that in order to avoid modifications to this list from any outside sources.
}
void addTasks(task) {
_tasks.add(task); // This is simply how you add anything to a list
notifyListeners(); // This is why we use providers. This function notifies all the children widgets
// of the Widget where we initiated our provider (see the parent of MaterialApp Widget in the above code)
// This is why changes to data should be made within this class only as it extends ChangeNotifier,
// which provides us with notifyListeners() method. Which ultimately notifies the widgets that the data has been modified and its time to rebuild the widgets that rely on this data!
}
}
You can copy paste this code, just make sure to add the provider package in your pubspec.yaml file as shown below.
When i create a list, i can only use it in the class where i created the list. But in another class i get an error 'undefined name' when i want to use the list. How can i get access to the list?
For example in my code i created a list 'plans' with strings.
class _PlanOverviewState extends State<PlanOverview> {
List<String> plans = ['Plan A', 'Plan B'];
void addPlan(String neuerPlan) {
setState(() {
plans.add(neuerPlan);
});
Navigator.of(context).pop();
}
Now I want to output a single string from the list plans in another Widget in the Appbar as title, so the User know where he is.
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(plans[i]))
How can i get access to the list plans?
One option is to create & use an InheritedWidget-style accessor for your State class and then you can access it from any descendant context.
import 'package:flutter/material.dart';
class InheritedWidgetPage extends StatefulWidget {
#override
_InheritedWidgetPageState createState() => _InheritedWidgetPageState();
static _InheritedWidgetPageState of(BuildContext context) =>
context.findAncestorStateOfType<_InheritedWidgetPageState>();
}
class _InheritedWidgetPageState extends State<InheritedWidgetPage> {
List<String> plans = ['Plan A', 'Plan B'];
#override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage(),
);
}
}
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
_InheritedWidgetPageState _state = InheritedWidgetPage.of(context);
return Scaffold(
appBar: AppBar(
title: Text(_state.plans[0]),
),
body: Center(
child: ElevatedButton(
child: Text('Goto ${_state.plans[1]}'),
onPressed: () => Navigator.of(context).push(
MaterialPageRoute(builder: (context) => PlanPage(1))),
),
),
);
}
}
class PlanPage extends StatelessWidget {
final int index;
PlanPage(this.index);
#override
Widget build(BuildContext context) {
_InheritedWidgetPageState _state = InheritedWidgetPage.of(context);
return Scaffold(
appBar: AppBar(
title: Text(_state.plans[index]),
),
body: Center(
child: Text('You are here: ${_state.plans[index]}'),
),
);
}
}
This can be pretty confusing to understand at first, but will make more sense as you get more familiar with Flutter's declarative framework.
For the above example to work, you need to have a MaterialApp ancestor widget, and your State class (where you're holding your plans state object) needs to be its parent. I explain why on a similar question here.
Your other option is to use a State Management package of which there are lots, which can help you simplify access to state objects.
I have 2 separate files in my Flutter application, main.dart and class1.dart
class1.dart is defined in my models/ folder
I am trying to access the List that I created from the SampleWidget class but I am unsure how to do this. IntelliSense wasn't able to find the List I had made from the instance object of SampleWidget.
What I'm trying to achieve is:
AssetImage(SampleWidget.listSampleWidget[0].foo)
class1.dart
class SampleWidget {
final String foo;
final int bar;
SampleWidget({this.foo, this.bar});
}
List<SampleWidget> listSampleWidget = [
SampleWidget(
foo: 'assets/001.png',
bar: 420,
),
];
main.dart is just the default boilerplate code when creating a new Flutter application,
main.dart
import 'package:flutter/material.dart';
import 'package:sandbox1/models/class1.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final extSampleClass = SampleWidget();
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
// horizontal).
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
//AssetImage(<pass SampleWidget foo parameter from the List here>),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
How do I reference to the List from the class here? Is something like this even doable or is there a different approach?
AssetImage() cannot be used as a standalone widget. Using Image.asset() worked. I can call the List object directly without having to reference the class apparently.
So the code will look something like:
Image.asset(listSampleWidget[0].foo)
And I got the picture on my test device!
So I am following along a book about Flutter App Development where I was tasked to implement a ToDoMenuItem class and create a list in it.
class TodoMenuItem {
final String title;
final Icon icon;
TodoMenuItem({this.title, this.icon});
List<TodoMenuItem> foodMenuList = [
TodoMenuItem(title: 'Fast Food', icon: Icon(Icons.fastfood)),
TodoMenuItem(title: 'Remind Me', icon: Icon(Icons.add_alarm)),
TodoMenuItem(title: 'Flight', icon: Icon(Icons.flight)),
TodoMenuItem(title: 'Music', icon: Icon(Icons.audiotrack)),
];
}
Then I was tasked to map it to a PopUpMenuButtonWidget using an itemBuilder. Here is the class I wrote for it.
class PopupMenuButtonWidget extends StatelessWidget
implements PreferredSizeWidget {
const PopupMenuButtonWidget({
Key key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
color: Colors.lightGreen.shade100,
height: preferredSize.height,
width: double.infinity,
child: Center(
child: PopupMenuButton<TodoMenuItem>(
icon: Icon(Icons.view_list),
onSelected: ((valueSelected) {
print('valueSelected: ${valueSelected.title}');
}),
itemBuilder: (BuildContext context) {
return foodMenuList.map((TodoMenuItem todoMenuItem) {
return PopupMenuItem<TodoMenuItem>(
value: todoMenuItem,
child: Row(
children: <Widget>[
Icon(todoMenuItem.icon.icon),
Padding(
padding: EdgeInsets.all(8.0),
),
Text(todoMenuItem.title),
],
),
);
}).toList();
},
),
),
);
}
#override // implement preferredSize
Size get preferredSize => Size.fromHeight(75.0);
}
However, it returns an error at this line.
return foodMenuList.map((TodoMenuItem todoMenuItem) {
And the error says
Undefined name 'foodMenuList'.
Try correcting the name to one that is defined, or defining the name.
How can I 'map' the foodMenuList list to the widget?
Your foodMenuList is declared in todoMenuItem class, while you try to refer to it like it would be a part of PopupMenuButtonWidget (you are doing this.foodMenuList in context of PopupMenuButtonModget)
You could create instantiate an instance of TodoMenuList in PopupMenuButtonWidget and then use it.
final TodoMenuItem _todoMenu = TodoMenuItem();
Widget build(BuildContext context) {
...
// Someplace where you need to use the list
_todoMenu.foodMenuList
...
}
I am new to flutter and to the concept of Object orientation in general. I am building a list of Text to be used with a CupertinoPicker in flutter, I want to use the same style for all the list items but I don't want to keep repeating the lines and each time specifying the text style.
For example, see the list of car manufacturers below:
import 'package:flutter/material.dart';
TextStyle kStyle = TextStyle(color: Colors.white, fontWeight: FontWeight.w900);
List<Text> manufacturers = [
Text('Toyota', style: kStyle,),
Text('VolksWagen', style: kStyle,),
Text('Nissan', style: kStyle,),
Text('Renault', style: kStyle,),
Text('Mercedes', style: kStyle,),
Text('BMW', style: kStyle,)
];
You see the list items in manufacturers list can get so long with more cars, can I use a class to tell flutter that my style is fixed to kstyle for all the items without explicitly writing style: kstyle for every single line?
Basically We can use DefaultTextStyle widget
Final Result
CupertinoPicker Widget
Common Column Widget
1. The Problem is we need to use CupertinoPicker
which in the library, it is defined as
final Widget result = DefaultTextStyle(
style: CupertinoTheme.of(context).textTheme.pickerTextStyle,
child: Stack(
2. Solution : Override Theme
Therefore We are required to defined its style at the very beginning definition of our app
const TextStyle kStyle = TextStyle(
color: Colors.blue,
fontWeight: FontWeight.w900,
);
class FlutterApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Cupertino Picker',
home: ListViewScreen(),
theme: ThemeData(
cupertinoOverrideTheme: CupertinoThemeData( // <---------- this
textTheme: CupertinoTextThemeData(
pickerTextStyle: kStyle,
),
),
),
);
}
}
A. Full Working Code
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
void main() {
runApp(FlutterApp());
}
const TextStyle kStyle = TextStyle(
color: Colors.blue,
fontWeight: FontWeight.w900,
);
class FlutterApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Cupertino Picker',
home: ListViewScreen(),
theme: ThemeData(
cupertinoOverrideTheme: CupertinoThemeData( // <---------- this
textTheme: CupertinoTextThemeData(
pickerTextStyle: kStyle,
),
),
),
);
}
}
class ListViewScreen extends StatelessWidget {
final List<Text> manufacturers = [
Text('Toyota'),
Text('VolksWagen'),
Text('Nissan'),
Text('Renault'),
Text('Mercedes'),
Text('BMW')
];
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text("Select Car"),
),
child: Container(
height: 200,
child: CupertinoPicker(
itemExtent: 50,
onSelectedItemChanged: (int index) {
print(index);
},
children: manufacturers,
),
),
);
}
}
B. [Optional] Simple use of Default Text Style
List<Text> manufacturers = [
Text('Toyota'),
Text('VolksWagen'),
Text('Nissan'),
Text('Renault'),
Text('Mercedes'),
Text('BMW')
];
const TextStyle kStyle = TextStyle(
color: Colors.white,
fontWeight: FontWeight.w900,
);
class CarList extends StatelessWidget {
#override
Widget build(BuildContext context) {
return DefaultTextStyle(
style: kStyle,
child: Column(
children: manufacturers,
),
);
}
}
You can create a new widget:
class MyTextWidget extends StatelessWidget {
final String text;
const MyTextWidget({Key key, this.text}) : super(key: key);
#override
Widget build(BuildContext context) {
return Text(text,style: TextStyle(color: Colors.white, fontWeight: FontWeight.w900),);
}
}
and use it in your list
List<Text> manufacturers = [
MyTextWidget('Toyota'),
MyTextWidget('VolksWagen'),
MyTextWidget('Nissan'),
MyTextWidget('Renault'),
MyTextWidget('Mercedes'),
MyTextWidget('BMW')
];
If you want to change the font in all the application, you must change it from MaterialApp like this:
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
textTheme: TextTheme(
//Use the appropriate TextStyle
),
),);
}
You can create one method for all Text widgets:
Text styledText(String text) => Text(text, style: TextStyle(color: Colors.white, fontWeight: FontWeight.w900));
and use it in your list:
List<Text> manufacturers = [
styledText('Toyota'),
styledText('VolksWagen'),
styledText('Nissan'),
styledText('Renault'),
styledText('Mercedes'),
styledText('BMW')
];
You can also create a list of strings
List<String> manufacturers = [
'Toyota',
'VolksWagen',
'Nissan',
'Renault',
'Mercedes',
'BMW',
];
And use tis method when you iterate through your list.
Or you can create a class instead of the method:
class CustomStyledText extends StatelessWidget {
final String text;
const CustomStyledText(this.text, {Key key}) : super(key: key);
TextStyle get _style => TextStyle(color: Colors.white, fontWeight: FontWeight.w900);
#override
Widget build(BuildContext context) => Text(text, style: _style);
}
You can create an extension on the Text widget and use that:
Create an extension:
// extension
extension on Text {
// method to apply style
applyStyle(TextStyle textStyle) {
return Text(
this.data,
style: textStyle,
);
}
}
Use the extension method on the Text widget:
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
...List.generate(
manufacturers.length,
// call the applyStyle method on the Text widget
(index) => manufacturers[index].applyStyle(kStyle),
).toList(),
],
),
);