I am trying to conditionally render widgets based on a state value in MyApp. My idea was the state value would be an integer (1-3). I would have a Map at the top of my project that looks like this:
Map _widgetType = {
1: WidgetOne(),
2: WidgetTwo(),
3: WidgetThree()
};
And then I have a state value called _type that is initialized at 1. I have a function that updates this value based on a selection from a list of options.
void _toggleClipped(int type) {
setState(() {
_type = type;
});
}
The build looks like the below.
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const <Widget>[
Text(
"Hi! I'm Matt!",
style: TextStyle(fontSize: 25),
),
_widgetType[_type],
Text(
"Click the button below to clip image!",
textAlign: TextAlign.center,
style: TextStyle(fontSize: 25))
],
),
),
);
}
The _widgetType[_type] would be the associated widget being rendered based upon the value for _type.
Is this doable in Flutter? I am getting an error saying The values in a const list literal must be constants. Try removing the keyword 'const' from the list literal.
Related
I am trying to better understand Dart data structures to use in Flutter. When using a map() to turn a list into widgets, I have seen that you add toList() at the end because map() uses lazy loading and only iterates when it needs to. However, I have also seen that using a spread operator[...] also does the trick.
Going off this SO post, I wanted to know what the best practice is in these situations.
My code is long so I have produced a similar workable example below. I need to use spread operator to create the ElevatedButton's inside Column, but code works with and without toList().
Do I use toList() or is the only purpose for including it is to achieve the same thing the spread operator is already doing?
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
var answers = [
'Answer 1',
'Answer 2',
'Answer 3',
];
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('My example'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text('The Question',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
)),
...answers.map(((e) => CustomButton(e)))
// .toList() // should this be included?
],
),
)),
);
}
}
class CustomButton extends StatelessWidget {
final String anAnswer;
CustomButton(this.anAnswer);
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () {},
child: Text(anAnswer),
);
}
}
I'd simply replace your final children element as:
children: [
Text('The Question',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
)),
for (final e in answers) CustomButton(e),
],
which uses a list literal for-loop to build the elements. No need for a spread operator as you are inserting the elements directly into the outer list.
I Want to make a ListView where it shows all icons from a Map Literal with its name...But I don't know how to get key and value inside ListView.builder
is there any index facility of map keys and value...
here is my code
class _HomeScreenState extends State<HomeScreen> {
Map<String, IconData> icons = {
'ac_unit': Icons.ac_unit,
'ac_unit_sharp': Icons.ac_unit_sharp,
'ac_unit_rounded': Icons.ac_unit_rounded,
'ac_unit_outlined': Icons.ac_unit_outlined,
'access_alarm': Icons.access_alarm
};
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: [
Expanded(
child: ListView.builder(
itemCount: icons.length,
itemBuilder: (context,index)
{
return Padding(
padding: EdgeInsets.symmetric(vertical: 10.0),
child: ListTile(
onTap: (){},
leading: CircleAvatar(
backgroundColor: Colors.blue,
child: Icon(//how to fetch Icon from map),
title: Text('How to fetch icon name from map',style: TextStyle(fontSize: 30.0,color: Colors.blue),),
),
);
}),
),
],
),
),
);
}
}
Map in dart have two iterator properties, named keys and values
keys contains every key inside a map
and same for values, it contains all values inside a map
You can get the icons like this:
//Store the icons in a list like this
var list = icons.values.toList();
//And access it individually like this
Icon(list[index]);
In your case, you want to get both icon name and icon value from your map. So, you do something like this:
var data = icons.entries.toList();
//Access key like this
data[index].key
//Access value like this
data[index].value
Following question:
I managed to get a lot further with my dosage calculator app and the state management procedure and now I'm trying to scale things up visually speaking.
So I wanted to change the built widget based on a dropdown menu which actually worked out fine but I'm trying to implement an AnimatedSwitcher so every time the user changes the dropdown menu, the old widget fades out and the new one in instead of just switching. Searched for solutions, found one but I don't know if I implemented it the right way, since I'm not getting any animation, but no error message neither.
I'm supposing I either used the wrong child or something like a unique key is missing (which I don't know how to implement)
Here are the necessary parts of my code:
DropdownMenu:
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0),
child:DropdownButtonHideUnderline(
child: DropdownButton<String>(
value: selectedItem,
onChanged: (String string) => setState(() {
streamController.sink.add(string);
return selectedItem = string;
}),
selectedItemBuilder: (BuildContext context) {
return items.map<Widget>((String item) {
return Text(item,
//style: TextStyle(fontWeight: FontWeight.bold),
textAlign: TextAlign.right,
);
}).toList();
},
items: items.map((String item) {
return DropdownMenuItem<String>(
child: Text('$item',
//style: TextStyle(fontWeight: FontWeight.bold),
textAlign: TextAlign.right,
),
value: item,
);
}).toList(),
),
),
);
}
}
StreamBuilder and AnimatedSwitcher:
StreamBuilder(
stream: streamController.stream,
builder: (context, snapshot) {
return AnimatedSwitcher(
duration: Duration(seconds: 1),
child: updateBestandteile(snapshot.data),
);
},
),
Example of condition:
Padding updateBestandteile(String i) {
switch (i) {
case "MMF":
{
return Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
height: 200,
decoration: BoxDecoration(
color: b,
borderRadius: BorderRadius.circular(10.0)
),
child: Align(
alignment: Alignment.center,
child: Row(
children: [
Column(
children: [
Text('Zu verwendende Präparate:',
style: TextStyle(fontWeight: FontWeight.bold)),
Text('Medetomidin 1mg/ml'),
Text('Midazolam 5mg/ml'),
Text('Fentanyl 0.5mg/ml'),
Text('NaCl 0,9%'),
],
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
),
Column(
children: [
Text('Anzumischende Menge:',
style: TextStyle(fontWeight: FontWeight.bold),
),
Text((MedetomidindosierungmgprokgKGW*selectedamount*selectedweight/(1000*Medetomidinmgproml)).toString()+"ml"),
Text((MidazolamdosierungmgprokgKGW*selectedamount*selectedweight/(1000*Midazolammgproml)).toString()+"ml"),
Text((FentanyldosierungmgprokgKGW*selectedamount*selectedweight/(1000*Fentanylmgproml)).toString()+"ml"),
Text((((MedetomidindosierungmgprokgKGW*selectedamount*selectedweight/(1000*Medetomidinmgproml))+(MidazolamdosierungmgprokgKGW*selectedamount*selectedweight/(1000*Midazolammgproml))+(FentanyldosierungmgprokgKGW*selectedamount*selectedweight/(1000*Fentanylmgproml)))*4).toString()+"ml"),
],
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
),
],
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
),
),
),
);
}
break;
Hope you might be able to help as you did last time :) Thanks in advance!
Cheers,
P
The issue might be that you are not setting a key. If the new child widget is of the same type as the old widget type then AnimatedSwitcher will NOT animate between them since as as far as the framework is concerned, they are the same widget. Set a unique ValueKey on each child child widget that you wish to animate.
Please refer to Flutter Docs for AnimatedSwitcher and check out the AnimatedSwitcher Widget of the Week video by Flutter Team.
If the "new" child is the same widget type and key as the "old" child,
but with different parameters, then AnimatedSwitcher will not do a
transition between them, since as far as the framework is concerned,
they are the same widget and the existing widget can be updated with
the new parameters. To force the transition to occur, set a Key on
each child widget that you wish to be considered unique (typically a
ValueKey on the widget data that distinguishes this child from the
others).
I've recently started using Flutter just for fun, and I'm stuck on adding actual functionality to the code without having everything inside one class.
Essentially, I'm trying to use a FloatingActionButton to increment the value of a Text Widget which stores the value of the user's level as an integer, but I don't want to have the whole app as a StatefulWidget because only the level is going to be updated. When the button is pressed, the value should increment by 1 and then show the new value on the screen.
I have the Level Text Widget inside a StatefulWidget class along with a function to update the level by one and set the state; the MaterialApp inside a StatelessWidget class; and the main body code inside another StatelessWidget class.
If this isn't the best way to do it please do let me know so I can improve for future projects, thanks.
main.dart
import 'package:flutter/material.dart';
main() => runApp(Start());
/// The Material App
class Start extends StatelessWidget{
#override
Widget build(BuildContext context){
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
backgroundColor: Colors.grey[800],
appBar: AppBar(
title: Text("Home Page"),
backgroundColor: Colors.cyan,
centerTitle: true,
),
floatingActionButton: FloatingActionButton(
onPressed: () {},
backgroundColor: Colors.orange,
child: Icon(Icons.add, color: Colors.black,),
),
body: HomePage(),
),
);
}
}
/// Main Content for the page (body)
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
// removed other children so there's less code to scan through for you :)
Padding(
padding: EdgeInsets.fromLTRB(30, 0, 0, 0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
// Text that just says "Level"
Text(
"Level",
style: TextStyle(
color: Colors.orange,
fontWeight: FontWeight.bold,
fontSize: 32,
),
),
// space between text and actual level value
SizedBox(height: 10),
// Create new level widget
Level(),
],
),
),
],
),
);
}
}
/// Updating level using a Stateful Widget
class Level extends StatefulWidget{
#override
State<StatefulWidget> createState(){
return _LevelState();
}
}
class _LevelState extends State<Level>{
int level = 0;
void incrementLevel(){
setState(() {
level += 1;
});
}
#override
Widget build(BuildContext context){
return Text(
"$level",
style: TextStyle(
color: Colors.grey[900],
fontWeight: FontWeight.normal,
fontSize: 28,
),
);
}
}
It actually is a weird way of doing it. However, there is various ways of achieving this
To give an example:
You can use KEYs to remotely redraw the child state
If you want an advanced solution that can assist you in bigger projects. You can use state management tecniques. You can find a lot of tutorials in the internet but these are some of them. BLOC, Provider, InheritedWidget.
Basicaly all of them does the same thing. Lifts up the state data so the place of the redrawn widget on the widget tree will not be important.
I strongly encourage you to watch some tutorials starting with the Provider. I hope this helps
I'm new to flutter,
So I'm working on a Flutter app & I Have My Functions & Widget File named: 'Function' Separated from my Main but I'm trying to set state in 'Main' from 'Function'
I have tried to set a global variable inside a MyText class Widget inside 'Function' and import 'Main'==> Function & Vice Versa at the same time, but I can't seem to manipulate the GlobalKey variable which would trigger setState again
(Class & MyText class Has since been Scrapped)
I also tried to set the function from my other file to the button like so
floatingActionButton: functions.random()
And somehow was able to set state (Sorry I Forgot How), but it kept running without being pressed
'Main.dart' Sample Code
String display = "";
class _MyHomePageState extends State<MyHomePage> {
var currentScreen = display;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
AutoSizeText(
currentScreen,
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: functions.menu(),
);
} //Build
}// _MyHomePageState
'Function.dart' Sample Code
SpeedDial menu(){
return SpeedDial(
SpeedDialChild(
child: Icon(Icons.autorenew),
backgroundColor: Colors.lightBlueAccent,
label: 'New Activity',
labelStyle: TextStyle(fontSize: 18.0),
onTap: () => random(),
),
)
random(){
...
return someString;
}
Intended Result
When Child 'New Activity' is clicked, setState is called with the variable currentScreen's state being set to the result from the function 'random()'
Thank You In Advance!
I don't know if this can finish your problem, but the approach will be a little different from your approach because I'm not really familiar with the globalsetkey works...
so from my perspective I'm using the bloc pattern so hope this can give you some inspiration how to resolve this problems
in classBLoc.dart
BehaviorSubject<bool> _dialClicked = BehaviorSubject<bool>();
Observable<bool> get dialClicked => _dialClicked.stream;
Function(bool) get dialClickedListener => _dialClicked.sink.add;
in main.dart
section floatingActionButton,
floatingActionButton: functions.menu(currentScreen),
in Function.dart
Widget menu(){
final bloc = Provider.of<classBLoc>();
return StreamBuilder(stream: bloc.dialClicked, initialData: false,builder: (context, snapshot){
return SpeedDial(
SpeedDialChild(
child: Icon(Icons.autorenew),
backgroundColor: Colors.lightBlueAccent,
label: 'New Activity',
labelStyle: TextStyle(fontSize: 18.0),
onTap: () => snapshot.data ? currentScreen : random(), // this used for checking data if have already been clicked or not
);
}),
);
Hope this can you some inspiration how it's worked //sorry if there is wrong bracketed