Related
Question: I am building an world time app and i am getting an error that type 'Null' is not a subtype of type 'String'.i am using variable isdaytime to change the background image in the app according to the time of the country, at day time backgroung image will be day and at night the bg image will be of night. but it seems the string is getting Null somewhere. Please help me in solving the doubt
Error image
Main.dart
import 'package:flutter/material.dart';
import 'package:universal_time_app/pages/choose_location.dart';
import'package:universal_time_app/pages/home.dart';
import'package:universal_time_app/pages/loading.dart';
void main() => runApp(MaterialApp(
initialRoute: '/home',
routes: {
'/':(context)=>Loading(),
'/home':(context)=>Home(),
'/location':(context)=>ChooseLocation(),
},
));
Choose_location.dart
import 'package:path/path.dart';
import 'package:flutter/material.dart';
import'package:universal_time_app/services/universal_time.dart';
//import 'package:universal_time_app/pages/loading.dart';
//import 'package:universal_time_app/pages/home.dart';
class ChooseLocation extends StatefulWidget {
const ChooseLocation({Key? key}) : super(key: key);
#override
_ChooseLocationState createState() => _ChooseLocationState();
}
List<UniversalTime> locations = [
UniversalTime(url: 'Europe/London', location: 'London', flag: 'UK.png',time:'',isdaytime: 1),
UniversalTime(url: 'Europe/Athens', location: 'Athens', flag: 'Greece.png',time:'',isdaytime: 1),
UniversalTime(url: 'Africa/Cairo', location: 'Cairo', flag: 'Egypt.png',time:'',isdaytime: 1),
UniversalTime(url: 'Africa/Nairobi', location: 'Nairobi', flag: 'Kenya.png',time:'',isdaytime: 1),
UniversalTime(url: 'America/Chicago', location: 'Chicago', flag: 'usa.png',time:'',isdaytime: 1),
UniversalTime(url: 'America/New_York', location: 'New York', flag: 'usa.png',time:'',isdaytime: 1),
UniversalTime(url: 'Asia/Seoul', location: 'Seoul', flag: 'South_Korea.png',time:'',isdaytime: 1),
UniversalTime(url: 'Asia/Jakarta', location: 'Jakarta', flag: 'Indonesia.png',time:'',isdaytime: 1),
UniversalTime(url: 'Asia/Kolkata', location: 'Delhi', flag: 'India.png',time:'',isdaytime: 1),
];
//void updateTime(snapshot,context);
void updateTime(index,context) async {
//var instance = locations[index];
UniversalTime instance = locations[index];
await instance.getTime();
//navigate to home screen
Navigator.pop(context, {
'location': instance.location,
'flag': instance.flag,
'time': instance.time,
'isdaytime': instance.isdaytime
});
}
class _ChooseLocationState extends State<ChooseLocation> {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.blueGrey,
appBar: AppBar(
backgroundColor: Colors.greenAccent,
title: Text('Choose a location'),
centerTitle: true,
elevation: 0,
),
body: ListView.builder(
itemCount:locations.length,
itemBuilder: (context,index) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 10.0,horizontal:10.0 ),
child: Card(
child: ListTile(
onTap: () {
updateTime(index,context);
},
title: Text(locations[index].location),
leading: CircleAvatar(
backgroundImage: AssetImage('assets/Flag_of_${locations[index].flag}'),
),
),
),
);
}
),
);
}
}
home.dart
import 'package:flutter/material.dart';
import 'package:universal_time_app/services/universal_time.dart';
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
Map data={};
#override
Widget build(BuildContext context) {
data=data.isEmpty ? data: ModalRoute.of(context)?.settings.arguments as Map;
print(data);
print('hi');
String? bgImage='Nw1.jpg';
//set bg image
if (data['isdaytime'] == 1) {
bgImage = 'Dw.jpg';
}
//print(bgImage);
//String bgImage=data['isdaytime']?'Dw.jpg':'Nw1.jpg';
return
Scaffold(
//backgroundColor: Colors.lightBlueAccent[100],x
body:Container(
decoration: BoxDecoration(
image: DecorationImage(
image:AssetImage('assets/$bgImage'),
fit: BoxFit.cover,
),
),
child: SafeArea(
child:Column(
children:<Widget>[
TextButton.icon(
onPressed:() async {
dynamic result = await Navigator.pushNamed(context, '/location');
setState(() {
data ={
'time' : result['time'],
'location' : result['location'],
'isdatime':result['isdaytime'],
'flag': result['flag'],
};
});
},
icon:Icon(Icons.edit_location_rounded,size:45.0,color: Colors.orange,),
label: Text(
'Edit Location',
style: TextStyle(
fontSize: 25.0,
color: Colors.white,
),
),
),
SizedBox(height: 230.0,),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
data['location'],
style: TextStyle(
color: Colors.yellowAccent,
fontSize: 50.0,
letterSpacing: 2.0,
),
),
],
),
SizedBox(height: 20.0,),
Text(
data['time'],
style: TextStyle(
color: Colors.white,
fontSize: 70.0,
letterSpacing: 2.0,
),
),
],
),
),
),
);
}
}
loading.dart
import 'package:flutter/material.dart';
import 'package:universal_time_app/services/universal_time.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
class Loading extends StatefulWidget {
const Loading({Key? key}) : super(key: key);
#override
_LoadingState createState() => _LoadingState();
}
class _LoadingState extends State<Loading> {
void setupWorldTime () async {
UniversalTime instance=UniversalTime(location: 'Berlin',time: '', flag: 'Germany.png', url:'Europe/berlin',isdaytime:0);
await instance.getTime();
Navigator.pushReplacementNamed(context, '/home',arguments: {
'location':instance.location, 'flag':instance.flag,'time':instance.time,'isdaytime':instance.isdaytime,
});
}
#override
void initState(){
super.initState();
setupWorldTime();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.greenAccent,
body:Center(
child: SpinKitPouringHourGlassRefined(
color: Colors.white,
size: 100.0,
),
),
);
}
}
universal_time.dart
import 'package:http/http.dart';
import 'dart:convert';
import 'package:intl/intl.dart';
class UniversalTime {
String location;
String time;
String flag;
String url;
int isdaytime;
UniversalTime({
required this.time,required this.isdaytime,
required this.location,required this.flag,required this.url});
Future <void> getTime() async {
//make request
Response response = await get(Uri.parse('http://worldtimeapi.org/api/timezone/$url'));
Map data = jsonDecode(response.body);
String datetime = data['datetime'];
String offset1 = data['utc_offset'].substring(1,3);
String offset2 = data['utc_offset'].substring(4,6);
DateTime now = DateTime.parse(datetime);
now = now.add(Duration(hours: int.parse(offset1), minutes: int.parse(offset2)));
isdaytime=now.hour > 5 && now.hour < 20 ? 1:0;
print(isdaytime);
//print('aakash');
//Set the time property
time=DateFormat.jm().format(now);
//print(time);
}
}
I could be wrong, as this is a complex app and I can't exactly run it right now, however the error code that you got indicates that it got a null value when attempting to read a value on line 75 on home.dart (I know this because #0 on your stack was home.dart and it shows the line number)
this is line 75: data['location'] this tells me that data does not have a location key, as I said, I could be wrong, and hopefully print(data) on line 19 should be able to disprove this theory. So before doing anything else, I would check to see what that print statement says. Anyway, here is my guess as to why this is happening to you.
data=data.isEmpty ? data: ModalRoute.of(context)?.settings.arguments as Map;
this line says: if data is empty, data should be equal to data, else, data should be equal to ModalRoute..., I'd bet you got it backwards, it should be:
data=data.isEmpty ? ModalRoute.of(context)?.settings.arguments as Map: data;
I also think its possible that home is being sent with no arguments as an initial route, but you would know that better than I.
The error message is an indication that somewhere in the code, you have assigned null to a var that requires a non-nullable String. Based on the code provided above, my best guess would be the result of your navigator in home.dart for the result['location'] or result['time'] is returning null
I'm trying to achieve a coplanar/disallined card collection layout in flutter. This is on Card layout on material Design documentation https://imgur.com/miHhpFs
I''ve tried with a GridView.count layout, but can't figure out how to disalline items. I also found out that there are user created libraries like this https://pub.dev/packages/flutter_staggered_grid_view that can help with what I want to do, but I'd prefer an official solution since this layout is on the material design documentation.
I don't know what the "official solution" for creating this is, but I believe it would be something along the lines of "Create three list views with three scroll controllers, one of which is offset from the other two, and then sync up their scroll controllers, accounting for the offset."
I didn't know if this would work or not, so I created this dartpad in order to test it:
https://dartpad.dev/f9c8f00b78899d3c8c4a426d3466a8a3
Just in case the dartpad doesn't work, here is the code that I used:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: ScrollSync(),
);
}
}
class ScrollSync extends StatefulWidget {
#override
_ScrollSyncState createState() => _ScrollSyncState();
}
class _ScrollSyncState extends State<ScrollSync> {
CustomScrollController _controller1 =
CustomScrollController(keepScrollOffset: true);
CustomScrollController _controller2 = CustomScrollController(
initialScrollOffset: 150.0, keepScrollOffset: true);
CustomScrollController _controller3 =
CustomScrollController(keepScrollOffset: true);
#override
void initState() {
_controller1.addListener(() =>
_controller2.jumpToWithoutGoingIdleAndKeepingBallistic(
_controller1.offset, "1 listen 2"));
_controller1.addListener(() =>
_controller3.jumpToWithoutGoingIdleAndKeepingBallistic(
_controller1.offset, "1 listen 3"));
_controller2.addListener(() =>
_controller1.jumpToWithoutGoingIdleAndKeepingBallistic(
_controller2.offset, "2 listen 1"));
_controller2.addListener(() =>
_controller3.jumpToWithoutGoingIdleAndKeepingBallistic(
_controller2.offset, "2 listen 3"));
_controller3.addListener(() =>
_controller1.jumpToWithoutGoingIdleAndKeepingBallistic(
_controller3.offset, "3 listen 1"));
_controller3.addListener(() =>
_controller2.jumpToWithoutGoingIdleAndKeepingBallistic(
_controller3.offset, "3 listen 2"));
super.initState();
}
#override
void dispose() {
_controller1.dispose();
_controller2.dispose();
_controller3.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Scroll Sync"),
),
body: Row(
children: [
Flexible(
flex: 1,
child: ListView.builder(
controller: _controller1,
scrollDirection: Axis.vertical,
itemBuilder: (_, index) => Container(
color: Colors.blueGrey,
width: 150,
height: 300,
margin: EdgeInsets.all(10.0),
child: Center(
child: Text(
"$index",
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.display2,
),
),
),
),
),
Flexible(
flex: 1,
child: ListView.builder(
controller: _controller2,
itemBuilder: (_, index) => Container(
height: 300,
color: Colors.blueGrey,
margin: EdgeInsets.all(10.0),
child: Center(
child: Text(
"$index",
style: Theme.of(context)
.textTheme
.display2
.copyWith(color: Colors.white),
),
),
),
),
),
Flexible(
flex: 1,
child: ListView.builder(
controller: _controller3,
itemBuilder: (_, index) => Container(
height: 300,
color: Colors.blueGrey,
margin: EdgeInsets.all(10.0),
child: Center(
child: Text(
"$index",
style: Theme.of(context)
.textTheme
.display2
.copyWith(color: Colors.black),
),
),
),
),
),
],
),
);
}
}
class CustomScrollController extends ScrollController {
CustomScrollController(
{double initialScrollOffset = 0.0,
keepScrollOffset = true,
debugLabel,
String controller})
: super(
initialScrollOffset: initialScrollOffset,
keepScrollOffset: keepScrollOffset,
debugLabel: debugLabel,
);
#override
_SilentScrollPosition createScrollPosition(
ScrollPhysics physics,
ScrollContext context,
ScrollPosition oldPosition,
) {
return _SilentScrollPosition(
physics: physics,
context: context,
oldPosition: oldPosition,
initialPixels: initialScrollOffset,
);
}
void jumpToWithoutGoingIdleAndKeepingBallistic(
double value, String controller) {
assert(positions.isNotEmpty, 'ScrollController not attached.');
for (_SilentScrollPosition position
in new List<ScrollPosition>.from(positions))
position.jumpToWithoutGoingIdleAndKeepingBallistic(value, controller);
}
}
class _SilentScrollPosition extends ScrollPositionWithSingleContext {
_SilentScrollPosition({
ScrollPhysics physics,
ScrollContext context,
ScrollPosition oldPosition,
double initialPixels,
}) : super(
physics: physics,
context: context,
oldPosition: oldPosition,
initialPixels: initialPixels,
);
void jumpToWithoutGoingIdleAndKeepingBallistic(
double value, String controller) {
print(controller);
print(value);
print(pixels);
if (controller[0] == "2") {
if (pixels + 150.0 != value) {
forcePixels(value - 150.0);
}
} else if (controller[9] == "2") {
if (pixels - 150.0 != value) {
forcePixels(value + 150.0);
}
} else if (pixels != value) {
forcePixels(value);
}
}
}
I've got a gridView where each grid has a FlatButton inside it. The button is supposed to trigger the visibility for another button I have outside the GridView. I've set the state in onPressed to change the bool showCard for everytime the GridView button is pressed. In my print statement, it's saying that it's working, producing true and false each time the button is pressed, but it's not changing the visibility of the other button called 'CheckoutCard()'. Can anyone help me?
import 'package:flutter/material.dart';
import 'package:flutter/painting.dart';
import 'package:bee/Cards/Items%20Card.dart';
import 'package:bee/Constants/Constants.dart';
import 'package:bee/MerchantCategories/My Categories.dart';
import 'package:bee/MenuButtons/Color Changer.dart';
import 'package:bee/Cards/Checkout Card.dart';
import 'package:bee/main.dart';
import 'Basket Menu.dart';
class MyMenu extends StatefulWidget {
#override
_MyMenuState createState() => _MyMenuState();
}
class _MyMenuState extends State<MyMenu> {
// bool showCard = _MyButtonState().showCard;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
brightness: Brightness.light,
leading: new IconButton(
icon: new Icon(Icons.arrow_back, color: Colors.black),
onPressed: () { Navigator.of(context).pop();},
),
backgroundColor: Colors.white,
title: Padding(
padding: const EdgeInsets.all(6.0),
child: Image(image: AssetImage('images/Merchants/My_Image.png'),),
),
elevation: 1.0,
centerTitle: true,
actions: <Widget>[
Padding(
padding: const EdgeInsets.only(right: 15.0),
child: IconButton(icon: Icon(Icons.shopping_basket, color: Colors.blue[800],),
onPressed: (){ Navigator.push(context, MaterialPageRoute(builder: (context){return BasketMenu();})); }
),
),
],
),
body: Stack(
children: <Widget>[
Flex(
direction: Axis.vertical,
children: <Widget>[
Expanded(
flex: 1,
child: Padding(
padding: const EdgeInsets.all(5.0),
child: Container(
child: ListView(children: <Widget>[
MyCategories(categoryText: Text('Restaurants', style: categoryTextStyle),),
MyCategories(categoryText: Text('Bars', style: categoryTextStyle),),
MyCategories(categoryText: Text('Games', style: categoryTextStyle),),
],
scrollDirection: Axis.horizontal,
),
),
),
),
Expanded(
flex: 10,
child: Container(
child: GridView.count(
crossAxisCount: 2,
children: <Widget>[
ItemsCard(
categoryName: Text('Fast Food', style: secondCategoryTextStyle,),
itemText: FastFoodText,
priceText: Text('£21.67', style: priceTextStyle,),
gridOutline: MyButton(
tile: GridTile(
child: FastFoodImage,
),
),
),
ItemsCard(
itemText: SnubbText,
priceText: Text('£44.95', style: priceTextStyle,),
gridOutline: MyButton(
tile: GridTile(
child: SnubbImage,
),
),
),
ItemsCard(
itemText: FreshText,
priceText: Text('£41.23', style: priceTextStyle,),
gridOutline: MyButton(
tile: GridTile(
child: FreshImage,
),
),
),
Container(),
],
),
),
),
],
),
Visibility(visible: _MyButtonState().showCard ? _MyButtonState().showCard : !_MyButtonState().showCard, child: CheckoutCard()),
],
),
);
}
}
class MyButton extends StatefulWidget {
#override
State<StatefulWidget> createState(){
return _MyButtonState();
}
MyButton({this.tile});
final GridTile tile;
bool isVisible = false;
int itemNumber = 0;
bool showCheckoutCard(){
return isVisible = !isVisible;
}
int itemCounter(){
return itemNumber++;
}
}
class _MyButtonState extends State<MyButton> {
bool changeColor = false;
static var myNewButtonClass = MyButton();
bool showCard = myNewButtonClass.isVisible;
#override
Widget build(BuildContext context) {
return FlatButton(
shape: Border(bottom: BorderSide(color: changeColor ? Colors.blue[800] : Colors.transparent, width: 3.0)),
child: widget.tile,
onPressed: (){
setState(() {
changeColor = !changeColor;
myNewButtonClass.itemCounter();
print(myNewButtonClass.itemCounter());
setState(() {
showCard = !showCard;
print(showCard);
});
});
},
);
}
}
You are calling the setState method inside your Button. I don't think it will change the state of your MyMenu widget. I would suggest you to change your Button as following:
class MyButton extends StatelessWidget {
final Color color;
final GridTile tile;
final VoidCallback onPressed;
const MyButton({Key key, this.color, this.tile, this.onPressed})
: super(key: key);
#override
Widget build(BuildContext context) {
return FlatButton(
shape: Border(bottom: BorderSide(color: color)),
child: tile,
onPressed: onPressed,
);
}
}
After that, you need to declare two variable in your MyMenu widget as follows:
class _MyMenuState extends State<MyMenu> {
bool changeColor = false;
bool showCard = false;
#override
Widget build(BuildContext context) {
yourBuildMethod()
In your MyMenu widget you can call button like this:
MyButton(
tile: GridTile(child: SnubbImage),
color: changeColor
? Colors.blue[800]
: Colors.transparent,
onPressed: () {
setState(() {
changeColor = !changeColor;
showCard = !showCard;
});
},
),
And now check your Visibility like this:
Visibility(
visible: showCard,
child: CheckoutCard(),
)
Now your variables are in your MyMenu widget and you are calling setState function in MyMenu widget. So it will be able to update the state of your widget. I hope this will be helpful for you.
To trigger a rebuild of your view based when changing the value of a variable you need to use setState.
Where you are are changing the value of the isVisible variable, you need to surround it with a setState:
setState(() {
isVisible = !isVisible;
});
Using HTML I can add a bullet points to a paragraph like this:
<ul>
<li> example </li>
<li> example </li>
<li> example </li>
<ul>
How can I write bullet point form in Flutter?
new Text(''),
If you don't want to download another library (e.g. flutter_markdown), and one or more of your list items have lengthy text that spans several rows, I'd go with Raegtime's answer. However, since it assumes a string with line breaks, I want to make a version for a list with strings, which is a more common scenario. In the code below, Column makes the list items come on different rows, and Row makes the bullet points have empty space below themselves.
import 'package:flutter/material.dart';
class UnorderedList extends StatelessWidget {
UnorderedList(this.texts);
final List<String> texts;
#override
Widget build(BuildContext context) {
var widgetList = <Widget>[];
for (var text in texts) {
// Add list item
widgetList.add(UnorderedListItem(text));
// Add space between items
widgetList.add(SizedBox(height: 5.0));
}
return Column(children: widgetList);
}
}
class UnorderedListItem extends StatelessWidget {
UnorderedListItem(this.text);
final String text;
#override
Widget build(BuildContext context) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text("• "),
Expanded(
child: Text(text),
),
],
);
}
}
We can use it as such:
UnorderedList([
"What conclusions can we draw from the implementation?",
"Are there any changes that need to be introduced permanently?"
])
And get the result:
Using markdown for this is overkill. Using • character is by far easier.
If you're too lazy to copy paste the character, here's a custom Text that does it for you:
class Bullet extends Text {
const Bullet(
String data, {
Key key,
TextStyle style,
TextAlign textAlign,
TextDirection textDirection,
Locale locale,
bool softWrap,
TextOverflow overflow,
double textScaleFactor,
int maxLines,
String semanticsLabel,
}) : super(
'• ${data}',
key: key,
style: style,
textAlign: textAlign,
textDirection: textDirection,
locale: locale,
softWrap: softWrap,
overflow: overflow,
textScaleFactor: textScaleFactor,
maxLines: maxLines,
semanticsLabel: semanticsLabel,
);
}
I tried using flutter_markdown and it seems to work. And of course you can change it to numbered/ordered or unordered list as you want.
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:flutter/material.dart';
void main() => runApp(Demo());
class Demo extends StatelessWidget {
final testData = ["Example1", "Example2", "Example3", "Example100"];
#override
Widget build(BuildContext context) {
final _markDownData = testData.map((x) => "- $x\n").reduce((x, y) => "$x$y");
return MaterialApp(
home: Scaffold(
body: Container(
margin: EdgeInsets.all(40.0),
child: Markdown(data: _markDownData)),
));
}
}
I would better use utf-code. For list I think more comfortably will be something like:
class DottedText extends Text {
const DottedText(String data, {
Key key,
TextStyle style,
TextAlign textAlign,
TextDirection textDirection,
Locale locale,
bool softWrap,
TextOverflow overflow,
double textScaleFactor,
int maxLines,
String semanticsLabel,
}) : super(
'\u2022 $data',
key: key,
style: style,
textAlign: textAlign,
textDirection: textDirection,
locale: locale,
softWrap: softWrap,
overflow: overflow,
textScaleFactor: textScaleFactor,
maxLines: maxLines,
semanticsLabel: semanticsLabel,);
}
#Snurrig - Excellent answer. Works great! Thanks a lot!
Modified it to create an ordered/numbered list, as well.
See below:
class OrderedList extends StatelessWidget {
OrderedList(this.texts);
final List<dynamic> texts;
#override
Widget build(BuildContext context) {
var widgetList = <Widget>[];
int counter = 0;
for (var text in texts) {
// Add list item
counter++;
widgetList.add(OrderedListItem(counter, text));
// Add space between items
widgetList.add(SizedBox(height: 5.0));
}
return Column(children: widgetList);
}
}
class OrderedListItem extends StatelessWidget {
OrderedListItem(this.counter, this.text);
final int counter;
final String text;
#override
Widget build(BuildContext context) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text("$counter. "),
Expanded(
child: Text(text),
),
],
);
}
}
you can use like this:
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'\u2022',
style: TextStyle(
fontSize: 16,
height: 1.55,
),
),
SizedBox(
width: 5,
),
Expanded(
child: Container(
child: Text(
str,
textAlign: TextAlign.left,
softWrap: true,
style: TextStyle(
fontSize: 16,
color: Colors.black.withOpacity(0.6),
height: 1.55,
),
),
),
),
],
);
You can use LineSplitter, Row, Column, and the ASCII bullet point. All u need is a String with linebreaks.
String myStringWithLinebreaks = "Line 1\nLine 2\nLine 3";
Example in a ListTile
ListTile(
title: Text("Title Text"),
subtitle:
Column(
children: LineSplitter.split(myStringWithLinebreaks).map((o) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text("• "),
Expanded(
child: Text(o),
)
],
);
}).toList())),
If you do not require markdowns everywhere, and just want to use them in one or two places, then adding a package or writing that much code for it is not a suitable idea.
You can copy the DOT from websites like emojipedia and paste it in front of your text.
here is an example:
Text("⚈ Provide, operate, and maintain our website"),
This will add bullet. Use it in a row with text.
Container(width: 10, height: 10, decoration:
BoxDecoration(shape: BoxShape.circle, color: Colors.black),),
Row(
children: [
_buildBullet(),
const SizedBox(width: 5),
_buildText(),
],
),
SizedBox _buildBullet() {
return SizedBox(
height: 7,
width: 7,
child: TextButton(
style: TextButton.styleFrom(
backgroundColor: Color(0xFFF8B407),
shape: const CircleBorder(),
),
child: const Text(''),
onPressed: () {},
),
);
}
Text _buildText() {
return const Text(
'Lorem Ipsum is simply dummy text of the printing and typesetting',
style: TextStyle(fontSize: 24, color: Colors.white),
);
}
I am still new to Flutter. Is there an example of a material dropdown list text field? I saw the example on Material Text Field but I didn't find anywhere in the documentation on how to implement this. Thanks for your help on this.
UPDATED :
Text form field with a dropdown
var _currencies = [
"Food",
"Transport",
"Personal",
"Shopping",
"Medical",
"Rent",
"Movie",
"Salary"
];
FormField<String>(
builder: (FormFieldState<String> state) {
return InputDecorator(
decoration: InputDecoration(
labelStyle: textStyle,
errorStyle: TextStyle(color: Colors.redAccent, fontSize: 16.0),
hintText: 'Please select expense',
border: OutlineInputBorder(borderRadius: BorderRadius.circular(5.0))),
isEmpty: _currentSelectedValue == '',
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
value: _currentSelectedValue,
isDense: true,
onChanged: (String newValue) {
setState(() {
_currentSelectedValue = newValue;
state.didChange(newValue);
});
},
items: _currencies.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
),
),
);
},
)
Hope this helps!
You want the DropdownButton or DropdownButtonFormField
https://api.flutter.dev/flutter/material/DropdownButton-class.html
and the DropdownMenuItem
https://api.flutter.dev/flutter/material/DropdownMenuItem-class.html
return DropdownButtonFormField(
items: categories.map((String category) {
return new DropdownMenuItem(
value: category,
child: Row(
children: <Widget>[
Icon(Icons.star),
Text(category),
],
)
);
}).toList(),
onChanged: (newValue) {
// do other stuff with _category
setState(() => _category = newValue);
},
value: _category,
decoration: InputDecoration(
contentPadding: EdgeInsets.fromLTRB(10, 20, 10, 20),
filled: true,
fillColor: Colors.grey[200],
hintText: Localization.of(context).category,
errorText: errorSnapshot.data == 0 ? Localization.of(context).categoryEmpty : null),
);
Other answers have fully described what you need, but here is an example that puts it all together, this is a reusable dropdown textfield widget that allows you to specify a list of options of any type (without losing dart's beautiful type system).
class AppDropdownInput<T> extends StatelessWidget {
final String hintText;
final List<T> options;
final T value;
final String Function(T) getLabel;
final void Function(T) onChanged;
AppDropdownInput({
this.hintText = 'Please select an Option',
this.options = const [],
this.getLabel,
this.value,
this.onChanged,
});
#override
Widget build(BuildContext context) {
return FormField<T>(
builder: (FormFieldState<T> state) {
return InputDecorator(
decoration: InputDecoration(
contentPadding: EdgeInsets.symmetric(
horizontal: 20.0, vertical: 15.0),
labelText: hintText,
border:
OutlineInputBorder(borderRadius: BorderRadius.circular(5.0)),
),
isEmpty: value == null || value == '',
child: DropdownButtonHideUnderline(
child: DropdownButton<T>(
value: value,
isDense: true,
onChanged: onChanged,
items: options.map((T value) {
return DropdownMenuItem<T>(
value: value,
child: Text(getLabel(value)),
);
}).toList(),
),
),
);
},
);
}
}
And you may use it like this:
AppDropdownInput(
hintText: "Gender",
options: ["Male", "Female"],
value: gender,
onChanged: (String value) {
setState(() {
gender = value;
// state.didChange(newValue);
});
},
getLabel: (String value) => value,
)
Following Jeff Frazier's answer, You can have more customization by using DropdownButton2 or DropdownButtonFormField2 from DropdownButton2 package. It's based on Flutter's core DropdownButton with more options you can customize to your needs.
This answer provide a example using a DropdownButtonFormField a convenience widget that wraps a DropdownButton widget in a FormField.
Ideal if you are using a Material FormField
'Dropdown' may not be the correct word that you are using to describe the design of text field referred in your material design example.
Here is how to implement it in Flutter:
import 'package:flutter/material.dart';
void main() {
runApp(TextFieldExample());
}
class TextFieldExample extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Text Field Example',
home: HomePage(),
theme: ThemeData(
primaryColor: Colors.deepPurple,
accentColor: Colors.white,
),
);
}
}
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Text Field Example'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: <Widget>[
//Material example
TextField(
decoration: InputDecoration(
filled: true,
hintText: 'Enter text',
labelText: 'Default text field'),
controller: new TextEditingController(),
),
SizedBox(
height: 16.0,
),
//Alternate
TextField(
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: 'Enter text',
labelText: 'Text field alternate'),
controller: new TextEditingController(),
),
],
),
),
);
}
}
This sample app contains two different examples of text field design that shrink and expand the associated label.
Gif of sample app - click here