Related
I am writing my first Flutter App with some online tutorials and I found error that I can't fix it.
I am trying to add Navigation by Navigator, but I can't understand why it doesn't work.
Once I am using Navigator in GestureDetector and it works fine, but I don't know what I supposed to do in floatingActionButton to make it work the same way. Note(NoteMode.Adding, null) probably should be something else instead null, because this null is making error (error from title). Can someone explain me what I am doing wrong and what I don't undarstand
Note List
#override
_NoteListState createState(){return _NoteListState();}
}
class _NoteListState extends State<NoteList> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Notes"),
),
body: FutureBuilder(
future: NoteProvider.getNoteList(),
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
final notes = snapshot.data;
return ListView.builder(
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
Navigator.push(
context, MaterialPageRoute(builder: (context) =>
Note(NoteMode.Editing, (notes as dynamic)[index]))
);
},
child: Card(
child: Padding(
padding: const EdgeInsets.only(
top: 30.0, bottom: 30.0, left: 13, right: 22),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
_NoteTitle((notes as dynamic)[index]['title']),
Container(height: 3,),
_NoteText((notes as dynamic)[index]['text']),
],
),
),
),
);
},
itemCount: notes.length,
);
}
return Center(child: CircularProgressIndicator());
},
),
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => Note(NoteMode.Adding, null)));
},
child: Icon(Icons.add),
),
);
}
}
Note
enum NoteMode{
Editing,
Adding
}
class Note extends StatefulWidget{
final NoteMode noteMode;
final Map<String, dynamic> note;
Note(this.noteMode, this.note,);
#override
State<Note> createState() => _NoteState();
}
class _NoteState extends State<Note> {
final TextEditingController _titleController = TextEditingController();
final TextEditingController _textController = TextEditingController();
List<Map<String, String>> get _notes => NoteInheritedWidget.of(context).notes;
#override
void didChangeDependencies(){
if(widget.noteMode == NoteMode.Editing){
_titleController.text = widget.note['title'];
_textController.text = widget.note['text'];
}
super.didChangeDependencies();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
widget.noteMode == NoteMode.Adding ? 'Add note' : 'Edit note',
),
),
body: Padding(
padding: const EdgeInsets.all(40.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextField(
controller: _titleController,
decoration: InputDecoration(
hintText: "Note title",
),
),
Container(height: 8,),
TextField(
controller: _textController,
decoration: InputDecoration(
hintText: "Note text",
),
),
Container(height: 15,),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
_NoteButton('SAVE', Colors.lightBlue, (){
final title = _titleController.text;
final text = _textController.text;
if(widget.noteMode == NoteMode.Adding){
NoteProvider.insertNote({
'title': title,
'text': text
});
} else if (widget.noteMode == NoteMode.Editing){
NoteProvider.updateNote( {
'id': widget.note['id'],
'title': _titleController.text,
'text': _textController.text,
});
}
Navigator.pop(context);}),
_NoteButton('DISCARD', Colors.grey, (){Navigator.pop(context);}),
widget.noteMode == NoteMode.Editing ?
_NoteButton('DELETE', Colors.redAccent, () async {
await NoteProvider.deleteNote(widget.note['id']);
Navigator.pop(context);})
: Container(),
],
)
],
),
),
);
}
}
Either you have to pass Map in place of null because you are receiving a Map on that page
Navigator.push(context, MaterialPageRoute(builder: (context) => Note(NoteMode.Adding, {"key":"value"})));
or you have to make Map nullable as
class Note extends StatefulWidget{
final NoteMode noteMode;
final Map<String, dynamic>? note;
Note(this.noteMode, this.note,);
#override
State<Note> createState() => _NoteState();
}
I actually have a searchBar(autocomplete) that is working.
When i select a result, the displaySnack() is working, it displays a snackBar, but i would like to display the content of testList().
My goal is to understand how I can launch another widget, to be able to add new widget on the page again and again.
My final goal is once i have the selected value, to make an http request, get a list as return and display a listview.
The function is executed ( i can see it in debugger ) but doesn't display anything..
(i'm new to flutter, so please explain your response if possible :) )
onSuggestionSelected : yes i know that it is void..
import 'package:drawer/src/share/snack_bar.dart';
import 'package:flutter/material.dart';
import 'package:flutter_typeahead/flutter_typeahead.dart';
import '../models/post_model.dart';
import '../services/http_service.dart';
// import 'package:http/http.dart' as http;
class PostsPage extends StatelessWidget {
final String title;
const PostsPage({Key? key, required this.title}) : super(key: key);
static Future<List<Post>> filterList(String value) async {
List<Post> list = await HttpService.fetchPosts();
return list.where(
(x) => x.title.toLowerCase().contains(value.toLowerCase())).toList();
}
#override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(
title: Text(title),
),
body: SafeArea(
child: Container(
padding: EdgeInsets.all(16),
child: TypeAheadField<Post?>(
debounceDuration: Duration(milliseconds: 500),
hideSuggestionsOnKeyboardHide: false,
textFieldConfiguration: TextFieldConfiguration(
decoration: InputDecoration(
prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(),
hintText: 'Select the namespace...',
),
),
suggestionsCallback: filterList,
itemBuilder: (context, Post? suggestion) {
final user = suggestion!;
return ListTile(
title: Text(user.title),
);
},
noItemsFoundBuilder: (context) => Container(
height: 100,
child: Center(
child: Text(
'No Namespace Found.',
style: TextStyle(fontSize: 24),
),
),
),
onSuggestionSelected: (Post? suggestion) {
final user = suggestion!;
displaySnack(context, ' Namespace: '+user.title);
testList(context); ################################ HERE
},
),
),
),
);
}
Widget testList(BuildContext context) {
return ListView.separated(
separatorBuilder: (BuildContext context, int index) => const Divider(),
itemCount: 2,
itemBuilder: (context, index) {
return Card(
child: ListTile(
title: Text("ppp"),
subtitle: Text("ppp"),
leading: CircleAvatar(
backgroundImage: NetworkImage(
"https://images.unsplash.com/photo-1547721064-da6cfb341d50"))
));
});
}
I need that : https://prnt.sc/136njev
It is obvious that you want the widget to rebuild to show the result. The most straightforward method is to use StatefulWidget. So I use it in your case(You can also find lots of ways to manage the state List of state management approaches)
Change your PostsPage to a StatefulWidget and rebuild when the user is selected
Add a Column in your PostsPage and separate into 2 parts: TypeAheadField & Result
Result part can use FutureBuilder (which can show loading indicator when data is not ready)
PostsPage:
class PostsPage extends StatefulWidget {
final String title;
const PostsPage({Key? key, required this.title}) : super(key: key);
static Future<List<Post>> filterList(String value) async {
// skip
}
#override
_PostsPageState createState() => _PostsPageState();
}
class _PostsPageState extends State<PostsPage> {
Post? user;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: SafeArea(
child: Column(
children: [
Container(
padding: EdgeInsets.all(16),
child: TypeAheadField<Post>(
// ...
// skip
// ...
onSuggestionSelected: (Post? suggestion) {
setState(() {
user = suggestion!;
displaySnack(context, ' Namespace: '+user.title);
});
},
),
),
Expanded(child: MyResult(post: user)),
],
),
),
);
}
}
Result part:
(I make it an isolated StatelessWidget just for better reading. You can use the original method to build the widget)
class MyResult extends StatelessWidget {
const MyResult({
required this.post,
Key? key,
}) : super(key: key);
final Post? post;
Future<List<OtherObject>> getOtherObjects(Post? post) async{
if(post == null){
return [];
}else{
return Future.delayed(Duration(seconds:3),()=>[OtherObject(title: '001'),OtherObject(title: '002'),OtherObject(title: '003')]);
}
}
#override
Widget build(BuildContext context) {
return FutureBuilder<List<OtherObject>>(
future: getOtherObjects(post),
builder: (context, snapshot) {
if(snapshot.hasData && snapshot.connectionState == ConnectionState.done) {
final result = snapshot.data!;
return ListView.separated(
separatorBuilder: (BuildContext context,
int index) => const Divider(),
itemCount: result.length,
itemBuilder: (context, index) {
return Card(
child: ListTile(
title: Text(result[index].title),
subtitle: Text("ppp"),
leading: CircleAvatar(
backgroundImage: NetworkImage(
"https://images.unsplash.com/photo-1547721064-da6cfb341d50"),
),
),
);
},
);
}else {
return Center(child: CircularProgressIndicator());
}
}
);
}
}
So what you are doing is basically just creating a ListView with your testList() function call and doing nothing with it, but what you want to do is to have that widget show up on the screen, right?
Flutter doesn't just show Widget if you create a new one, you must tell it to render. Just imagine you are doing preparing Widgets (e.g. Widgets in Widgets) and Flutter would render it immediately to the screen without you being finished, that wouldn't be that great.
You need to push that Widget over the Navigator widget that Flutter provides you.
onSuggestionSelected: (Post? suggestion) {
final user = suggestion!;
displaySnack(context, ' Namespace: '+user.title);
Navigator.push(
context,
MaterialPageRoute(builder: (context) => testList(context)),
);
}
I suggest you to read this article to Navigation Basics.
you can use listView builder to show the selected results.
onSuggestionSelected: (Post? suggestion) {
final user = suggestion!;
displaySnack(context, ' Namespace: '+user.title);
//get results
var results = fetchResult(suggestion);
//return a listview of the results
return ListView.builder(
physics: const AlwaysScrollableScrollPhysics(),
shrinkWrap: true,
scrollDirection: Axis.vertical,
itemCount: results.length,
itemBuilder: (_context, index) {
Post post = results[index];
return Card(
elevation: 2,
child: InkWell(
child: Container(
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6.0),
border: Border.all(color: Colors.black),
),
child: DefaultTextStyle(
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 12,
color: Colors.white),
child: Row(children: [
Expanded(
flex: 2,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(post.data),
],
),
),
]),
),
),
onTap: () {
//do something when user clicks on a result
},
));
}
},
If you want to show a list of selected items then you will have to add a ListView in the widget tree. Also use a StatefullWidget instead of StatelessWidget, because whenever you select an item, the selected list gets changed thus state.
sample code for state
List<Post> selectedPosts;
#override
void initState() {
super.initState();
selectedPosts = [];
}
#override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(
title: Text(title),
),
body: SafeArea(
child: Column(
children: [
Container(
padding: EdgeInsets.all(16),
child: TypeAheadField<Post?>(
debounceDuration: Duration(milliseconds: 500),
hideSuggestionsOnKeyboardHide: false,
textFieldConfiguration: TextFieldConfiguration(
decoration: InputDecoration(
prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(),
hintText: 'Select the namespace...',
),
),
suggestionsCallback: filterList,
itemBuilder: (context, Post? suggestion) {
final user = suggestion!;
return ListTile(
title: Text(user.title),
);
},
noItemsFoundBuilder: (context) => Container(
height: 100,
child: Center(
child: Text(
'No Namespace Found.',
style: TextStyle(fontSize: 24),
),
),
),
onSuggestionSelected: (Post? suggestion) {
final user = suggestion!;
displaySnack(context, ' Namespace: '+user.title);
setState(()=> selectedPosts.add(suggestion));
},
),
),
testList(context),
],
),
),
);
and in the testList function change the itemcount
itemCount: 2,
to
itemCount: selectedPosts?.length ?? 0,
I have an app that makes a post request to an API with data about drivers and orders. The initial page displays a list of drivers in individual list tiles. The list tile has a drop down option. Clicking on that option brings you to a new page with a list view of orders for that driver. Clicking on an individual order brings you to a form. On submitting and validating this form, I want to change the color of that orders text from red to green. Each Order has a submitted flag, and when it submits I would want to change that to true and then have the color change. When all the orders are green within an List View, I want the color of that driver to turn green. I've been going over riverpod tutorials and documentation but can't quite figure out how to get this done. Can someone point me in the right direction?
main.dart
void main() {
runApp(ProviderScope(child: MyApp()));
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
debugShowCheckedModeBanner: false,
home: DriversPage(),
);
}
}
drivers.dart - This is where the drivers are displayed
class DriversPage extends StatelessWidget {
final HttpService httpService = HttpService();
var colorChoice = Colors.red;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xFFFAFAFA),
appBar: AppBar(
title: Text("Drivers")
),
body: Container(
child: FutureBuilder(
future: httpService.getOrders(),
builder: (BuildContext context, AsyncSnapshot<List<Order>> snapshot) {
if (snapshot.hasData) {
List<Order> orders = snapshot.data;
return ListView(
children: orders.map((Order order) => Card(child: ExpansionTile(
title: Text(order.driver, style: TextStyle(color: colorChoice),),
children: <Widget>[
Container(
alignment: Alignment.center,
margin: EdgeInsets.all(10.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(2.0),
border: Border.all(color: Colors.black26)
),
child: ListTile(title: Text("Orders"),
trailing: Icon(Icons.keyboard_arrow_right),
onTap: () => Navigator.of(context).push(MaterialPageRoute(builder: (context) => OrdersState(driverName: order.driver, driverOrders: order.orders))),),
),
],
))).toList(),
);
}
return Center(child: CircularProgressIndicator());
}),
));
}
}
orders.dart - This is where the orders for a driver are displayed. I originally had it as a stateful widget but turned it into a Consumer Widget and took an attempt at making a provider but was lost on how to handle it in a listview like this. As you can see here I am using the ternary operator for the text color based on item.submitted
final driverListProvider = StateNotifierProvider((ref) => new DriverListTest());
class OrdersState extends ConsumerWidget {
final String driverName;
final List<OrderElement> driverOrders;
const OrdersState({Key key, this.driverName, this.driverOrders}) : super(key: key);
#override
Widget build(BuildContext context, ScopedReader watch) {
return Scaffold(
appBar: AppBar(
title: Text(driverName),
),
body: ListView.builder(
itemCount: driverOrders.length,
itemBuilder: (context, index){
final item = driverOrders[index];
return Card(
key: UniqueKey(),
child: ListTile(title: Text(item.order, style: TextStyle(color: item.submitted? Colors.green : Colors.red),),
subtitle: Text('${item.company}\n${item.address}'),
onTap: () => Navigator.of(context).push(MaterialPageRoute(builder: (context) => OrderForm(orderTitle: item.order,))),));
}),
);
}
}
orderform.dart - Only showing one field for the form, figured the rest was not neccessary, just need to show what happens on submit.
class OrderForm extends StatefulWidget {
final String orderTitle;
const OrderForm({this.orderTitle});
#override
_OrderFormState createState() => _OrderFormState();
}
class _OrderFormState extends State<OrderForm> {
#override
final _formKey = GlobalKey<FormState>();
final _orderModel = Order();
List<String> _pickerNames = ['Loader 1', 'Loader 2', 'Loader 3', 'Loader 4'];
String _selectedPickedBy;
String _selectedCheckedBy;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(backgroundColor: Colors.blueGrey,title: Center(
child: Text(widget.orderTitle),
),),
floatingActionButton: FloatingActionButton(
child: Icon(
Icons.delete
),
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
scrollable: true,
title: Text('Login'),
content: Padding(
padding: const EdgeInsets.all(8.0),
child: Form(
child: Column(
children: <Widget>[
TextFormField(
decoration: InputDecoration(
labelText: 'Reason',
icon: Icon(Icons.account_box),
),
),
TextFormField(
decoration: InputDecoration(
labelText: 'Reason 1',
icon: Icon(Icons.email),
),
),
TextFormField(
decoration: InputDecoration(
labelText: 'Reason 2',
icon: Icon(Icons.message),
),
),
],
),
),
),
actions: [
RaisedButton(
child: Text("Submit"),
onPressed: () {
})
],
);
});
}
),
body: Container(
padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 16.0),
child: Builder(
builder: (context) => Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
DropdownButtonFormField(
isExpanded: true,
hint: Text('Picked By'),
value: _selectedPickedBy,
onChanged: (newValue){
setState(() {
_selectedPickedBy = newValue;
});
},
validator: (value) => value == null
? 'Picked By Required' : null,
items: _pickerNames.map((picker) {
return DropdownMenuItem(
child: new Text(picker),
value: picker,
);
}).toList(),
onSaved: (value) => setState(() => _orderModel.pickedBy = value) ,
),
Container(
padding: const EdgeInsets.symmetric(
vertical: 16.0, horizontal: 16.0
),
child: RaisedButton(
onPressed: (){
final form = _formKey.currentState;
if (form.validate()){
form.save();
Navigator.pop(context,);
}
},
child: Text("Submit"),
),
)
],
)),
),
)
);
}
}
ordermodel.dart - This is the model for the drivers and orders when making http requests to my api. At the bottom you can see where I attempt at making a statenotifier and what I'm trying to with accepting a list of OrderElement(The list of orders).
List<Order> orderFromJson(String str) => List<Order>.from(json.decode(str).map((x) => Order.fromJson(x)));
String orderToJson(List<Order> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class Order {
Order({
this.driver,
this.orders,
});
String driver;
List<OrderElement> orders;
factory Order.fromJson(Map<String, dynamic> json) => Order(
driver: json["Driver"],
orders: List<OrderElement>.from(json["Orders"].map((x) => OrderElement.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"Driver": driver,
"Orders": List<dynamic>.from(orders.map((x) => x.toJson())),
};
}
class OrderElement {
OrderElement({
this.order,
this.company,
this.address,
this.submitted,
this.index,
});
String order;
String company;
String address;
bool submitted;
num index;
factory OrderElement.fromJson(Map<String, dynamic> json) => OrderElement(
order: json["Order"],
company: json["Company"],
address: json["Address"],
submitted: json["submitted"],
index: json["index"]
);
Map<String, dynamic> toJson() => {
"Order": order,
"Company": company,
"Address": address,
};
}
class DriverListTest extends StateNotifier<List<OrderElement>> {
DriverListTest([List<OrderElement> drivers1]) : super(drivers1 ?? []);
void onSubmit(num index) {
state = [
for(final currentOrder in state)
if (currentOrder.index == index)
OrderElement(
order: currentOrder.order,
company: currentOrder.company,
address: currentOrder.address,
submitted: !currentOrder.submitted,
index: currentOrder.index,
)
else
currentOrder,
];
}
}
Don't know if my Http class is necessary but let me know if it is. I tried following https://www.refactord.com/guides/riverpod-state-management-explained and How to set the state of a widget at an index in a listview.builder in flutter how to handle individual widgets but again I just got lost. Any help would be greatly appreciated! Thanks in advance.
This is my full class code i am retrieving data from mySQL and showing it on screen but i am continiously getting this error that , The getter 'length' was called on null.
Receiver: null
Tried calling: length , i can't find on which line i am doing wrong and what i am doing wrong everything is looking good , what i am doing wrong please help !
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:http/http.dart' show get;
import 'dart:convert';
class Spacecraft {
final String id;
final String name , experience , img_url , rating, contact, description;
Spacecraft({
this.id,
this.name,
this.experience,
this.img_url,
this.rating,
this.contact,
this.description
});
factory Spacecraft.fromJson(Map<String, dynamic> jsonData) {
return Spacecraft(
id: jsonData['id'],
name: jsonData['name'],
experience: jsonData['experience'],
img_url: "http://sha-way.freeweb.pk/images/"+jsonData['img_url'],
rating: jsonData['rating'],
contact: jsonData['contact'],
description: jsonData['description'],
);
}
}
class CustomListView extends StatelessWidget {
final List<Spacecraft> spacecrafts;
CustomListView([this.spacecrafts]);
Widget build(context) {
return ListView.builder(
itemCount: spacecrafts.length,
itemBuilder: (context, int currentIndex) {
return createViewItem(spacecrafts[currentIndex], context);
},
);
}
Widget createViewItem(Spacecraft spacecraft, BuildContext context) {
return new ListTile(
title: new Card(
elevation: 5.0,
child: new Container(
decoration: BoxDecoration(border: Border.all(color: Colors.orange)),
padding: EdgeInsets.all(20.0),
margin: EdgeInsets.all(20.0),
child: Column(
children: <Widget>[
Padding(
child: Image.network(spacecraft.img_url),
padding: EdgeInsets.only(bottom: 8.0),
),
Row(children: <Widget>[
Padding(
child: Text(
spacecraft.name,
style: new TextStyle(fontWeight: FontWeight.bold),
textAlign: TextAlign.right,
),
padding: EdgeInsets.all(1.0)),
Text(" | "),
Padding(
child: Text(
spacecraft.experience,
style: new TextStyle(fontStyle: FontStyle.italic),
textAlign: TextAlign.right,
),
padding: EdgeInsets.all(1.0)),
]),
],
),
),
),
onTap: () {
//We start by creating a Page Route.
//A MaterialPageRoute is a modal route that replaces the entire
//screen with a platform-adaptive transition.
var route = new MaterialPageRoute(
builder: (BuildContext context) =>
new SecondScreen(value: spacecraft),
);
//A Navigator is a widget that manages a set of child widgets with
//stack discipline.It allows us navigate pages.
Navigator.of(context).push(route);
});
}
}
//Future is n object representing a delayed computation.
Future<List<Spacecraft>> downloadJSON() async {
final jsonEndpoint =
"http://sha-way.freeweb.pk";
final response = await get(jsonEndpoint);
if (response.statusCode == 200) {
List spacecrafts = json.decode(response.body);
return spacecrafts
.map((spacecraft) => new Spacecraft.fromJson(spacecraft))
.toList();
} else
throw Exception('We were not able to successfully download the json data.');
}
class SecondScreen extends StatefulWidget {
final Spacecraft value;
SecondScreen({Key key, this.value}) : super(key: key);
#override
_SecondScreenState createState() => _SecondScreenState();
}
class _SecondScreenState extends State<SecondScreen> {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(title: new Text('Detail Page')),
body: new Container(
child: new Center(
child: Column(
children: <Widget>[
Padding(
child: new Text(
'BACHAY LOG KI DETAILS',
style: new TextStyle(fontWeight: FontWeight.bold,fontSize: 20.0),
textAlign: TextAlign.center,
),
padding: EdgeInsets.only(bottom: 20.0),
),
Padding(
//`widget` is the current configuration. A State object's configuration
//is the corresponding StatefulWidget instance.
child: Image.network('${widget.value.img_url}'),
padding: EdgeInsets.all(12.0),
),
Padding(
child: new Text(
'NAME : ${widget.value.name}',
style: new TextStyle(fontWeight: FontWeight.bold),
textAlign: TextAlign.left,
),
padding: EdgeInsets.all(20.0),
),
Padding(
child: new Text(
'EXPERIENCE : ${widget.value.experience}',
style: new TextStyle(fontWeight: FontWeight.bold),
textAlign: TextAlign.left,
),
padding: EdgeInsets.all(20.0),
)
], ),
),
),
);
}
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
theme: new ThemeData(
primarySwatch: Colors.deepOrange,
),
home: new Scaffold(
appBar: new AppBar(title: const Text('MySQL Images Text')),
body: new Center(
//FutureBuilder is a widget that builds itself based on the latest snapshot
// of interaction with a Future.
child: new FutureBuilder<List<Spacecraft>>(
future: downloadJSON(),
//we pass a BuildContext and an AsyncSnapshot object which is an
//Immutable representation of the most recent interaction with
//an asynchronous computation.
builder: (context, snapshot) {
if (snapshot.hasData) {
List<Spacecraft> spacecrafts = snapshot.data;
return new CustomListView(spacecrafts);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
//return a circular progress indicator.
return new CircularProgressIndicator();
},
),
),
),
);
}
}
This is the error i am getting
════════ Exception caught by widgets library ═══════════════════════════════════════════════════════
The following NoSuchMethodError was thrown building CustomListView(dirty):
The getter 'length' was called on null.
Receiver: null
Tried calling: length
You are making a little mistake.
Here you are defining spacecrafts as optional:
CustomListView([this.spacecrafts]);
But when you call it here:
List<Spacecraft> spacecrafts = snapshot.data;
return new CustomListView(spacecrafts);
You are calling it like it was obligatory parameter, so you are not really passing any parameter actually since you are not assign it at the correct variable.
If you want to keep that optional, you should call your CustomListView like:
List<Spacecraft> spacecrafts = snapshot.data;
return new CustomListView(spacecrafts:spacecrafts);
Otherwise, if there could be not case where the list could be omitted, change this:
CustomListView([this.spacecrafts]);
to this:
CustomListView(this.spacecrafts);
This is the error:
itemCount: spacecrafts.length,
Why?
Because it uses a null initialization
final List<Spacecraft> spacecrafts; **<-- this is null(empty) - no length**
CustomListView([this.spacecrafts]);
Widget build(context) {
return ListView.builder(
itemCount: spacecrafts.length, **<-- spacecrafts is null, has no data**
itemBuilder: (context, int currentIndex) {
return createViewItem(spacecrafts[currentIndex], context);
},
);
}
and you are updating the new spacecrafts List, in a different class
builder: (context, snapshot) {
if (snapshot.hasData) {
List<Spacecraft> spacecrafts = snapshot.data; **<-- new spacecrafts initialization in a dofferent Class**
return new CustomListView(spacecrafts);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
//return a circular progress indicator.
return new CircularProgressIndicator();
},
I am trying to render a search form above a list of items from Firestore and filter locally based on what is typed in the form.
I tried adding both widgets to the body like this, but it is only displaying the search form:
body: Column(
children: <Widget>[Searchform(), ContentWidget()],
),
This is the current code which displays a basic list:
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class Items extends StatefulWidget {
Items({Key key}) : super(key: key);
#override
_ItemsState createState() => _ItemsState();
}
class _ItemsState extends State<Items> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Search'),
),
body: ContentWidget(),
);
}
}
class Searchform extends StatelessWidget {
final TextEditingController _searchController = TextEditingController();
#override
Widget build(BuildContext context) {
return TextField(
controller: _searchController,
decoration: InputDecoration(
labelText: "Search",
hintText: "Search",
prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(15.0),
),
),
),
);
}
}
class ContentWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection('content').snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) return new Text('Error: ${snapshot.error}');
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return new Text('Loading...');
default:
return new ListView(
children:
snapshot.data.documents.map((DocumentSnapshot document) {
return new ListTile(
title: new Text(document['term']),
);
}).toList(),
);
}
},
);
}
}
What I was thinking of doing is saving the items in local state and filter them based on what is typed in the search box.
this is a very simple way try this code within "snapshot.data.documents.map((DocumentSnapshot document)"
if(_searchController.text.toString().contains(document['term'])){
return new ListTile(
title: new Text(document['term']),
);
}
I have provide simple filter record in listview code.
class FilterDemo extends StatefulWidget {
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return FilterState();
}
}
class FilterState extends State<FilterDemo> {
List<String> items, duplicateList;
TextEditingController editingController = TextEditingController();
#override
void initState() {
// TODO: implement initState
super.initState();
items = List<String>.generate(1000, (i) => "Item $i");
duplicateList = items;
}
void filterSearchResults(String query) {
List<String> dummySearchList = List<String>();
dummySearchList.addAll(duplicateList);
if (query.isNotEmpty) {
List<String> dummyListData = List<String>();
dummySearchList.forEach((item) {
if (item.contains(query)) {
dummyListData.add(item);
}
});
setState(() {
items.clear();
items.addAll(dummyListData);
});
return;
} else {
setState(() {
items.clear();
items.addAll(duplicateList);
});
}
}
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text("Filter Demo"),
),
body: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
onChanged: (value) {
filterSearchResults(value);
},
controller: editingController,
decoration: InputDecoration(
labelText: "Search",
hintText: "Search",
prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(25.0)))),
),
),
Expanded(
child: ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return ListTile(
title: Text('${items[index]}'),
);
},
),
),
],
),
);
}
}
I have provide Code how the saving the items in local state and filter them based on what is typed in the search box.
class UserList extends StatefulWidget {
final FirebaseUser user;
final String currentUserId;
UserList({this.currentUserId, this.user});
#override
_UserListState createState() => _UserListState();
}
class _UserListState extends State<UserList> {
TextEditingController _signUpConfirmPassword = new TextEditingController();
String _myValue = '';
UniqueKey _myKey = UniqueKey();
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text("UserList"),
),
child: ListView(
shrinkWrap: true,
children: <Widget>[
Padding(
padding: EdgeInsets.all(10.0),
child: CupertinoTextField(
keyboardType: TextInputType.text,
//inputFormatters: [LengthLimitingTextInputFormatter(60)],
placeholder: 'Search For..',
// placeholderStyle: TextStyle(
// fontWeight: FontWeight.w200
// ),
prefix: Padding(
padding: EdgeInsets.only(left: 10.0),
child: Icon(
Icons.search,
),
),
onChanged: (val) {
if (val.isNotEmpty) {
_myValue = val;
}
setState(() {
_myKey = UniqueKey();
});
},
decoration: BoxDecoration(
border: Border.all(color: primaryColor),
borderRadius: BorderRadius.circular(20.0)),
)),
SizedBox(height: 10.0),
Container(
key: _myKey,
child: FetchUsers(
user: widget.user,
myValue: _myValue,
)),
],
));
}
}
class FetchUsers extends StatefulWidget {
final String myValue;
final FirebaseUser user;
FetchUsers({this.myValue, this.user});
#override
_FetchUsersState createState() => _FetchUsersState();
}
class _FetchUsersState extends State<FetchUsers> {
List searchName = List();
List userName = List();
Future listOfUsers() {
if (widget.myValue.isEmpty) {
return Firestore.instance
.collection('users')
.where('Role', isEqualTo: 'user')
.orderBy('Created', descending: true)
.limit(10)
.getDocuments()
.then((d) {
userName.clear();
d.documents.forEach((f) {
userName.add(f);
});
return userName;
});
} else {
return Firestore.instance
.collection('users')
.where('Role', isEqualTo: 'user')
.limit(10)
.getDocuments()
.then((d) {
searchName.clear();
d.documents.forEach((f) {
if (f.data['Name']
.toString()
.toLowerCase()
.contains(widget.myValue.toLowerCase())) {
searchName.add(f);
}
});
return searchName;
});
}
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: listOfUsers(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CupertinoActivityIndicator(),
);
} else {
return ListView.separated(
physics: ClampingScrollPhysics(),
separatorBuilder: (context, int) {
return Divider();
},
itemCount: snapshot.data.length,
shrinkWrap: true,
padding: EdgeInsets.all(10.0),
itemBuilder: (context, index) {
return Card(
elevation: 7.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0)),
child: IntrinsicHeight(
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
' ${snapshot.data[index]['Name']}',
style: TextStyle(
color: outlineColor,
fontWeight: FontWeight.bold),
),
SizedBox(
height: 5.0,
),
Text(
' ${snapshot.data[index]['Email']}',
),
],
),
Spacer(),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
RaisedButton.icon(
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(20.0)),
color: primaryColor,
onPressed: () {
Navigator.push(
context,
CupertinoPageRoute(
builder: (context) => Chat(
user: widget.user,
name: snapshot.data[index]
['Name'],
peerId: snapshot.data[index]
['UID'],
)));
},
icon: Icon(
Icons.chat,
color: themeColor,
),
label: Text(
"Chat",
style: TextStyle(color: themeColor),
)),
RaisedButton.icon(
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(20.0)),
color: primaryColor,
onPressed: () {
Navigator.push(
context,
CupertinoPageRoute(
builder: (context) =>
SendNotificationOption(
name: snapshot.data[index]
['Name'],
myFcm: snapshot.data[index]
['UID'],
isBroadcast: false,
)));
},
icon: Icon(
Icons.notifications,
color: themeColor,
),
label: Text(
"Notification",
style: TextStyle(color: themeColor),
)),
],
),
],
),
),
));
},
);
}
},
);
}
}
What you have type in Search then that Data is shown in listview]1