I've been playing around trying to learn flutter and programming in general. I have a problem here that I have not been able to find a solution for on the forums
or anywhere else. I want to display the details of a FireStore Document on this view. The previous view is List view from Flutter only showing the Title.
The previous view(main) passes the document ID into this view as "partID." I have been able to successfully query that document for a snapshot and even print
out particular details. However, when I try to add the variables as the "existing text" in a form field, it doesn't recognize them.
What am I missing?
import 'dart:async';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'ItemData.dart';
class View extends StatefulWidget {
final String partID;
View({Key key, this.partID}): super (key: key);
#override
ViewState createState() => ViewState();
}
class ViewState extends State<View> {
Data newData = new Data();
#override
Widget build(BuildContext context) {
getItem();
return new Scaffold(
//CreateWidget()
appBar: AppBar(
backgroundColor: Colors.black,
title: Text("Item Data"),
),
body:
new Column(
children: <Widget>[
Flexible(
flex: 0,
child: Center(
child: Form(
//key: this._formKey,
child: Flex(
direction: Axis.vertical,
children: <Widget>[
ListTile(
title: TextFormField(
initialValue: newData.title,
decoration: new InputDecoration(
icon: new Icon(Icons.edit),
),
)
),
ListTile(
title: TextFormField(
initialValue: title,
decoration: new InputDecoration(
icon: new Icon(Icons.edit),
),
)
),
new Text("${widget.partID}"),
],
),
),
),
),
],
)
);
}
Future getItem() async {
DocumentSnapshot snapshot = await Firestore.instance.collection('items').document('${widget.partID}').get();
String title = snapshot['title'];
String location = snapshot.data['location'].toString();
print('${title}');
print('${location}');
}
}
For this you should be using something like a future builder as your body. It still allows loading asynchronously and you can also show a loading indicator. It also allows you to access all your values from the database within the child widgets.
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(title: new Text(title)),
body: new FutureBuilder(
future: Firestore.instance.collection('items').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) return const Text('Loading...');
return new Column(
children: <Widget>[
Flexible(
flex: 0,
child: Center(
child: Form(
//key: this._formKey,
child: Flex(
direction: Axis.vertical,
children: <Widget>[
ListTile(
title: TextFormField(
initialValue: snapshot.data["title"],
decoration: new InputDecoration(
icon: new Icon(Icons.edit),
),
)),
ListTile(
title: TextFormField(
initialValue: title,
decoration: new InputDecoration(
icon: new Icon(Icons.edit),
),
)),
new Text("${widget.partID}"),
],
),
),
),
),
],
);
}),
);
}
This is an example of how your code would be laid out when using a FutureBuilder.
Related
I am new to programming and while i was trying to create a small TODO app, i was able to save the data into cloud firestore but the problems ocurred when i tried to retrieve the data using Streambuilder. I was following old tutorials before null safety , so i suspect that issues are regarding the null safety.
The code works without any errors in android studio but the data from firestore can't be retrieved.
The code is as follows:
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class AddTODOlist extends StatelessWidget {
static String id = 'add_todolist';
final TextEditingController _controller = TextEditingController();
void _addUser(){
FirebaseFirestore.instance.collection("Todos").add({"title" : _controller.text});
_controller.text = "";
}
Widget _buildList(QuerySnapshot? snapshot){
return ListView.builder(
itemCount: snapshot!.docs.length,
itemBuilder: (context, index){
final doc = snapshot.docs[index];
final map = (doc.data()as dynamic)['title'];
return ListTile(
title: Text(map,style: TextStyle(color: Colors.black),),
);
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Loan System',),
centerTitle: true,
),
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Row(
children: [
Expanded(
child: TextField(
controller: _controller,
decoration: InputDecoration(
hintText: 'Add new user',
),
),
),
TextButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all<Color>(Colors.blue),
),
onPressed:(){
_addUser();
},
child: Text('Add',
style: TextStyle(
color: Colors.white,
),),
),
],
),
StreamBuilder<QuerySnapshot?>(
stream: FirebaseFirestore.instance.collection('todos').snapshots(),
builder: (context, snapshot){
if(!snapshot.hasData) return LinearProgressIndicator();
else {
return Expanded(
child: _buildList(snapshot.data),
);
}
}
),
],
),
),
),
);
}
}
You have an error in the spelling of the collection name. Remember that Firestore is case sensitive
Todos vs todos
FirebaseFirestore.instance.collection("Todos").add({"title" : _controller.text});
and here:
stream: FirebaseFirestore.instance.collection('todos').snapshots(),
Let me know if this does not help
I am using an autocomplete textfield in my flutter application. While typing text in the textfield the user gets the suggestions (via JSON). Then the user should click on a suggestion and should be forwarded to the "SecondPage". At the same time the country of the selected player should also be passed to the "SecondPage".
In the part itemSubmitted I tried to integrate my plan but it doesn't work. The "SecondPage" doesn't start. Can you help here?
This is my code:
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomPadding: false,
appBar: AppBar(
title: Text('Search'),
),
body: new Center(
child: new Column(children: <Widget>[
new Column(children: <Widget>[
searchTextField = AutoCompleteTextField<PlayersForSearch>(
style: new TextStyle(color: Colors.black, fontSize: 16.0),
decoration: new InputDecoration(
suffixIcon: Container(
width: 85.0,
height: 60.0,
),
contentPadding: EdgeInsets.fromLTRB(10.0, 30.0, 10.0, 20.0),
filled: true,
hintText: 'Search Player Name',
hintStyle: TextStyle(color: Colors.black)),
itemSubmitted: (item) {
SecondPage(item.country);
MaterialPageRoute(builder: (context) => SecondPage(item.country));
setState(() => searchTextField.textField.controller.text =
item.autocompleteterm);
},
clearOnSubmit: false,
key: key,
suggestions: PlayerViewModel.player_search,
itemBuilder: (context, item) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(item.autocompleteterm,
style: TextStyle(
fontSize: 16.0
),),
Padding(
padding: EdgeInsets.all(15.0),
),
Text(item.country,
)
],
);
},
itemSorter: (a, b) {
return a.autocompleteterm.compareTo(b.autocompleteterm);
},
itemFilter: (item, query) {
return item.autocompleteterm
.toLowerCase()
.startsWith(query.toLowerCase());
}),
]),
])));
}
I believe what's missing is the Navigator.push call to push the SecondPage onto the stack of routes. A MaterialPageRoute will not place itself onto the stack of pages/routes.
Example
When you focus on the text field and press Enter, it will navigate to the SecondPage with the value of the TextFormField.
import 'package:flutter/material.dart';
class NavTextFieldPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Nav TextField Submit'),
),
body: NavTextfieldExample(),
);
}
}
class NavTextfieldExample extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
TextFormField(
decoration: InputDecoration(
labelText: 'Navigate to next page',
),
initialValue: 'Japan',
onFieldSubmitted: (item) {
/// Using default Navigator from Scaffold, *push* onto stack SecondPage
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => SecondPage(item)));
},
)
],
);
}
}
class SecondPage extends StatelessWidget {
final String country;
SecondPage(this.country);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Nav Second Page'),
),
body: Center(
child: Text('Country: $country'),
),
);
}
}
The key piece above is:
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => SecondPage(item)));
which uses the Navigator object to push routes onto your stack of routes (i.e. pages).
please try the following :
. create a SecondPage.dart
. put in a stateless or stateful Widget
. create a route in your materialApp()
Creating an app in Flutter for a local resturant/pub/bistro and I want to display a card above all other cards in the menu to show temporary messages/deals. My implementation relies on a menu_screen and a "handler" of sorts (not fully developed mind you).
But there's two problems
It displays as a card within a card
the information is displayed twice when it should only be shown once at the top of the screen
Here's what it looks like when run on a device for testing:
Pastebin for menu_screen: enter link description here
// sets up the menu screen for our program
// imports
import 'package:flutter/material.dart';
import 'package:elehouseapp/handlers/food_menu_handler.dart';
// set up class
class Menus extends StatelessWidget{
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
child: Scaffold(
appBar: TabBar(
labelColor: Colors.black,
indicatorColor: Colors.black,
tabs: <Widget>[
Tab(
text: 'Food',
),
Tab(
text: 'Drink',
),
]
),
body: TabBarView(
children: <Widget>[
FoodHandler(),
//Text("Food"),
Text("Drink"),
],
),
),
);
}
}
Pastebin for food_menu_handler: enter link description here
import 'package:flutter/material.dart';
// Generates an 2D array of food items and places elements into cards
// Some data
final items = ['test','test2'];
final desc = ["Loreum Isplum","Other Test"];
final price = [2.20,20.00];
// Handles array data and puts into cards
class FoodHandler extends StatelessWidget{
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: items.length,
itemBuilder: (context, index){
return Card(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Visibility(
visible: true,
child: Card(
child: ListTile(
title: Text("COVID-19"),
subtitle: Text("Menu restrictions are in place"),
),
)
),
ListTile(
title: Text(items[index]),
subtitle: Text(desc[index]+"\n£"+price[index].toString()),
),
ButtonBar(
children: <Widget>[
FlatButton(
child: Text("Add to Basket"),
onPressed: null,
),
],
),
],
),
);
}
);
}
}
EDIT I've un-nested the card (as pointed out). Hopefully this may explain what I have with explanations of what I'm trying to do
You could change the ListView.builder in a normal ListView and generate the list of its children using conditions and loops inside the list as follow:
// Generates an 2D array of food items and places elements into cards
// Some data
final items = ['test', 'test2'];
final desc = ["Loreum Isplum", "Other Test"];
final price = [2.20, 20.00];
// Handles array data and puts into cards
class FoodHandler extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ListView(
children: [
if (1 == 1) // TODO your condition here
Card(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Card(
child: ListTile(
title: Text("COVID-19"),
subtitle: Text("Menu restrictions are in place"),
),
),
],
),
),
for (int index = 0; index < items.length; index++)
Card(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListTile(
title: Text(items[index]),
subtitle: Text(desc[index] + "\n£" + price[index].toString()),
),
ButtonBar(
children: <Widget>[
FlatButton(
child: Text("Add to Basket"),
onPressed: null,
),
],
),
],
),
),
],
);
}
}
I marked with a TODO the line where you have to insert the condition for the first "alert".
Also I suggest you to use a class to represent the list items instead of having three different lists and "joining" them with the index, it's a more convenient and elegant solution
I'm new to flutter, I trying to pass a value from textfield and when i click a button submit, display it in textformfield in another screen, my problem, I don't know the right way to get value
Some Code :
String txt = "";
TextEditingController controllerTxt = new TextEditingController();
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: Text('Create'),
actions: <Widget>[
FlatButton(
child: Text('Submit'),
textColor: Colors.white,
onPressed: () {
setState(() {
//txt = (controllerTxt.text);
Navigator.pushNamed(context, '/ResultPage');
});
},
),
],
),
body: new Container(
child: new Column(
children: <Widget>[
new TextField(
controller: controllerTxt,
maxLines: 5,
decoration: new InputDecoration(
),
),
],
),
),
);
}
}
class _ResultPageState extends State<ResultPage> {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: Text('Result'),
),
body: new Container(
padding: EdgeInsets.all(10.0),
child: new Column(
children: <Widget>[
new TextFormField(
decoration: InputDecoration(
labelText: 'Name :',
),
),
new Text("${controllerTxt.text}"),
],
),
),
);
}
}
I have done the same thing by passing data through the constructor
Navigator.push(context,
MaterialPageRoute(builder: (context) => ResultPage(controllerTxt.text)));
class ResultPage extends StatefulWidget {
final String result;
ResultPage(this.result);
I am looking for some guidance to create listviews with separators. For instance, I would like to take messages from a DB grouped by date and separate the messages by date with some graphic or line, etc... and then have the messages under the separator. Trying this in flutter and any guidance or push in the right direction would be appreciated.
simply put the ListItem into a Container and add decoration:
#override
Widget build(BuildContext context) {
return new Container(
child: new ListTile(
title: new Text('I have border')
),
decoration:
new BoxDecoration(
border: new Border(
bottom: new BorderSide()
)
)
);
}
I apologize for the ugliness of the design, but just to show you, you can pretty much build your own design that you desire, this a quick example:
import "package:flutter/material.dart";
import 'package:meta/meta.dart';
class Test extends StatefulWidget {
#override
_TestState createState() => new _TestState();
}
class _TestState extends State<Test> {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Test"),
),
body: new ListView.builder(
// itemCount: myData.lenght(),
itemCount: 20,
itemBuilder: (BuildContext context, int index) {
//sort my data by timestamp before building
return new CustomWidget(date: "Convert my time stamp to date",
content: "My Awesome Content",
trailingIconOne: new Icon(Icons.share, color: Colors.blueAccent,),
trailingIconTwo: new Icon(
Icons.favorite, color: Colors.redAccent,),);
}),
);
}
}
class CustomWidget extends StatelessWidget {
String date;
String content;
Icon trailingIconOne;
Icon trailingIconTwo;
CustomWidget(
{#required this.date, #required this.content, #required this.trailingIconOne, #required this.trailingIconTwo});
#override
Widget build(BuildContext context) {
return new Container(
decoration: new BoxDecoration(
border: new Border.all(color: Colors.grey[500])
),
child: new Column(
children: <Widget>[
new Container (child: new Text(date), color: Colors.yellow[200],),
new Container(height: 15.0,),
new Text(content),
new Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
new IconButton(icon: trailingIconOne, onPressed: () {}),
new Container(width: 10.0,),
new IconButton(icon: trailingIconTwo, onPressed: () {})
],
)
],
),
);
}
}
For a better design, you can get rid of the borders and use a Divider instead:
return new Container(
child: new Column(
children: <Widget>[
new Column (children: <Widget>[
new Container (child: new Text(date), color: Colors.yellow[200],),
new Container(height: 15.0,),
new Text(content),
new Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
new IconButton(icon: trailingIconOne, onPressed: () {}),
new Container(width: 10.0,),
new IconButton(icon: trailingIconTwo, onPressed: () {}),
], ),
new Divider(height: 15.0,color: Colors.red,),
],
)
],
),
And a better visual solution in my opinion is to use a Card instead of Container,
return new Card(
child: new Column(
children: <Widget>[
new Column (children: <Widget>[
new Container (child: new Text(date), color: Colors.yellow[200],),
new Container(height: 15.0,),
new Text(content),
new Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
new IconButton(icon: trailingIconOne, onPressed: () {}),
new Container(width: 10.0,),
new IconButton(icon: trailingIconTwo, onPressed: () {}),
], ),
// new Divider(height: 15.0,color: Colors.red,),
],
)
],
),
);
A much cleaner and simple solution would be to use the following
ListView.separated(
separatorBuilder: (context, index) => Divider(
color: Colors.black,
),
itemCount: 20,
itemBuilder: (context, index) => Padding(
padding: EdgeInsets.all(8.0),
child: Center(child: Text("Index $index")),
),
)
Long answer short
Just use Column and add Divider
ListView.builder(
itemCount: count,
itemBuilder: (context, int index) {return tile(index);},
),
Create a widget for itemBuilder
Widget tile(int index) {
return Column(
children: <Widget>[
// your widgets in listView
Divider(), //at last simply add divider
],
);
}
While this might not be applicable to your situation, a simple way to add dividers to a ListView is to use ListView.divideTiles:
// Adapted from https://flutter.io/flutter-for-android/#listviews--adapters
import 'package:flutter/material.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new ListExamplePage(),
);
}
}
class ListExamplePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
// https://docs.flutter.io/flutter/material/ListTile/divideTiles.html
var dividedWidgetList = ListTile.divideTiles(
context: context,
tiles: _getListData(),
color: Colors.black).toList();
return new Scaffold(
appBar: new AppBar(
title: new Text('List Example'),
),
body: new ListView(children: dividedWidgetList)
);
}
_getListData() {
List<Widget> widgets = [];
for (int i=0; i<100; i++) {
widgets.add(
new Padding(
padding: new EdgeInsets.all(10.0),
child: new Text('Row $i'))
);
}
return widgets;
}
}
EDIT:
For dynamically built lists, ListTile.divideTiles isn't the right choice. Adding a divider at the stage where the list item is built is probably the best way to go:
import 'package:flutter/material.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new ListExamplePage(),
);
}
}
class ListExamplePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('List Example'),
),
body: new ListView.builder(
itemBuilder: (BuildContext context, int position) =>
// Try using either _getRowWithDivider or _getRowWithBoxDecoration
// for two different ways of rendering a divider
_getRowWithDivider(position),
));
}
/// Returns the widget at position i in the list, separated using a divider
Widget _getRowWithDivider(int i) {
var children = <Widget>[
new Padding(padding: new EdgeInsets.all(10.0), child: new Text('Row $i')),
new Divider(height: 5.0),
];
return new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: children,
);
}
// Returns the widget at position i in the list, separated using a BoxDecoration
Widget _getRowWithBoxDecoration(int i) {
return new Container(
decoration: new BoxDecoration(
border:
new Border(bottom: new BorderSide(color: Colors.grey[100]))),
child: new Padding(
padding: new EdgeInsets.all(10.0), child: new Text('Row $i')));
}
}
A lot of overly complex answers here
Just use divideTiles:
new ListView(
children: ListTile.divideTiles(context: context, tiles:
Or alternatively use separated:
new ListView.separated(
itemCount: 25,
separatorBuilder: (BuildContext context, int index) => Divider(),
itemBuilder: (BuildContext context, int index) {
final Iterable<ListTile> products = widget.products.map((Product product) {
return productListAdapter(
product: product,
).build(context);
});
final listView = ListTile.divideTiles(
context: context, tiles: products, color: Colors.black12)
.toList();
return ListView(
padding: EdgeInsets.symmetric(vertical: 8.0), children: listView);
productListAdapter code is here
class productListAdapter extends StatelessWidget {
final Product product;
productListAdapter({this.product});
TextStyle getTextStyle() {
return TextStyle(
fontSize: 16.0,
color: Colors.black);
}
Color getColor(BuildContext context) {
return Theme.of(context).primaryColor;
}
}
#override
Widget build(BuildContext context) {
return ListTile(
title: Text(product.name),
leading: CircleAvatar(
child: Text(
product.name[0],
style: getTextStyle(),
),
backgroundColor: getColor(context),
),
);
}
}