I'm trying to implement same layout in flutter how can i achieve it, i have already tried using wrap widget but Textfield getting full width and changing textfield width dynamically based on content is not possible
I don't know if this is too late. But this library is exactly what you need. You have all steps on the website.
It's a library called Flutter Chips. There are the steps and I'll put the link from the library too.
https://pub.dev/packages/flutter_chips_input
First of all install the library:
dependencies:
flutter_chips_input: ^1.9.4
Here is the code part:
ChipsInput(
initialValue: [
AppProfile('John Doe', 'jdoe#flutter.io', 'https://d2gg9evh47fn9z.cloudfront.net/800px_COLOURBOX4057996.jpg')
],
decoration: InputDecoration(
labelText: "Select People",
),
maxChips: 3,
findSuggestions: (String query) {
if (query.length != 0) {
var lowercaseQuery = query.toLowerCase();
return mockResults.where((profile) {
return profile.name.toLowerCase().contains(query.toLowerCase()) || profile.email.toLowerCase().contains(query.toLowerCase());
}).toList(growable: false)
..sort((a, b) => a.name
.toLowerCase()
.indexOf(lowercaseQuery)
.compareTo(b.name.toLowerCase().indexOf(lowercaseQuery)));
} else {
return const <AppProfile>[];
}
},
onChanged: (data) {
print(data);
},
chipBuilder: (context, state, profile) {
return InputChip(
key: ObjectKey(profile),
label: Text(profile.name),
avatar: CircleAvatar(
backgroundImage: NetworkImage(profile.imageUrl),
),
onDeleted: () => state.deleteChip(profile),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
);
},
suggestionBuilder: (context, state, profile) {
return ListTile(
key: ObjectKey(profile),
leading: CircleAvatar(
backgroundImage: NetworkImage(profile.imageUrl),
),
title: Text(profile.name),
subtitle: Text(profile.email),
onTap: () => state.selectSuggestion(profile),
);
},
)
Related
If you make a new Flutter project and include the dependencies and then replace your main.dart file you should be where I am on this question.
I left the original load: with Future.delayed but it doesn't seem to matter. I know partially what my problem is but am unable to come up with a better solution.
1) I don't seem to be using my snapshot.data and instead I am just making a empty List with str and then i just addAll into it and use that. So i'd love to not do that, i originally was using snapshot.data but ran into problems when I tried to "pull to load more data" which happens after you scroll to the bottom of the list.
The problem with my current method of doing this is that if you pull to load more users and then try to pull again before the users have loaded, The app breaks and doesn't wait for the data to properly load. I believe that I need to be doing that all in the load: of this library easy_refresh... but I am not sure how to rewrite my code to accomplish that.
How can I get my data to load with snapshot.data and then when I pull to refresh, I append 100 more users to that list but the UI waits for the list to update before it finishes the load. Would I be better off just putting a Blocking UI element and after the str list updates? and when new users are loaded I unblock the UI? which sorta feels hackish and not the correct way to solve this. The plugin itself should be able to do the loading and when its ready it stops the spinner under the list and says "finished".
pubspec.yaml
dependencies:
flutter:
sdk: flutter
flutter_easyrefresh: ^1.2.7
http: ^0.12.0+2
main.dart
import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:convert';
import 'package:flutter_easyrefresh/easy_refresh.dart';
import 'package:http/http.dart' as http;
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
backgroundColor: Colors.white
),
home: DuelLeaderBoards(),
);
}
}
class DuelLeaderBoards extends StatefulWidget {
#override
_DuelLeaderBoardsState createState() => _DuelLeaderBoardsState();
}
class _DuelLeaderBoardsState extends State<DuelLeaderBoards> {
List<Entry> str = [];
GlobalKey<EasyRefreshState> _easyRefreshKey = new GlobalKey<EasyRefreshState>();
GlobalKey<RefreshHeaderState> _headerKey = new GlobalKey<RefreshHeaderState>();
GlobalKey<RefreshHeaderState> _connectorHeaderKey = new GlobalKey<RefreshHeaderState>();
GlobalKey<RefreshFooterState> _footerKey = new GlobalKey<RefreshFooterState>();
GlobalKey<RefreshFooterState> _connectorFooterKey = new GlobalKey<RefreshFooterState>();
Future<LeaderBoards> getLeaderBoards(start) async {
String apiURL = 'https://stats.quake.com/api/v2/Leaderboard?from=$start&board=duel&season=current';
final response = await http.get(apiURL);
if (response.statusCode == 200) {
final responseBody = leaderBoardsFromJson(response.body);
return responseBody;
} else {
throw Exception('Failed to load Data');
}
}
void updateLeaderBoardList(e) async {
setState(() {
str.addAll(e.entries);
});
}
#override
void initState() {
getLeaderBoards(0).then((onValue) => str = onValue.entries );
super.initState();
}
#override
Widget build(BuildContext context) {
Widget header = ClassicsHeader(
key: _headerKey,
refreshText: "pullToRefresh",
refreshReadyText: "releaseToRefresh",
refreshingText: "refreshing...",
refreshedText: "refreshed",
moreInfo: "updateAt",
bgColor: Colors.transparent,
textColor: Colors.white,
);
Widget footer = ClassicsFooter(
key: _footerKey,
loadHeight: 50.0,
loadText: "pushToLoad",
loadReadyText: "releaseToLoad",
loadingText: "loading",
loadedText: "loaded",
noMoreText: "Finished",
moreInfo: "updateAt",
bgColor: Colors.transparent,
textColor: Colors.white,
);
return FutureBuilder(
future: getLeaderBoards(0),
builder:
(BuildContext context, AsyncSnapshot<LeaderBoards> snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
} else {
return Builder(builder: (BuildContext context) {
return Center(
child: new EasyRefresh(
key: _easyRefreshKey,
behavior: ScrollOverBehavior(),
refreshHeader: ConnectorHeader(
key: _connectorHeaderKey,
header: header,
),
refreshFooter: ConnectorFooter(
key: _connectorFooterKey,
footer: footer,
),
child: CustomScrollView(
semanticChildCount: str.length,
slivers: <Widget>[
SliverList(
delegate: SliverChildListDelegate(<Widget>[header]),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
return new Container(
height: 70.0,
child: Card(
child: new Text(
'${index+1}: ${str[index].userName}',
style: new TextStyle(fontSize: 18.0),
),
));
},
childCount: str.length,
)),
SliverList(
delegate: SliverChildListDelegate(<Widget>[footer]),
)
],
),
onRefresh: () async {
await new Future.delayed(const Duration(seconds: 0), () {
setState(() {});
});
},
loadMore: () async {
getLeaderBoards(str.length).then((onValue) => {
updateLeaderBoardList(onValue)
});
},
// loadMore: () async {
// await new Future.delayed(const Duration(seconds: 0), () {
// getLeaderBoards(str.length).then((onValue) => {
// updateLeaderBoardList(onValue)
// });
// });
// },
)
);
});
}
});
}
}
LeaderBoards leaderBoardsFromJson(String str) {
final jsonData = json.decode(str);
return LeaderBoards.fromJson(jsonData);
}
String leaderBoardsToJson(LeaderBoards data) {
final dyn = data.toJson();
return json.encode(dyn);
}
class LeaderBoards {
String boardType;
List<Entry> entries;
int totalEntries;
LeaderBoards({
this.boardType,
this.entries,
this.totalEntries,
});
factory LeaderBoards.fromJson(Map<String, dynamic> json) => new LeaderBoards(
boardType: json["boardType"] == null ? null : json["boardType"],
entries: json["entries"] == null ? null : new List<Entry>.from(json["entries"].map((x) => Entry.fromJson(x))),
totalEntries: json["totalEntries"] == null ? null : json["totalEntries"],
);
Map<String, dynamic> toJson() => {
"boardType": boardType == null ? null : boardType,
"entries": entries == null ? null : new List<dynamic>.from(entries.map((x) => x.toJson())),
"totalEntries": totalEntries == null ? null : totalEntries,
};
}
class Entry {
String userName;
int eloRating;
String profileIconId;
String namePlateId;
Entry({
this.userName,
this.eloRating,
this.profileIconId,
this.namePlateId,
});
factory Entry.fromJson(Map<String, dynamic> json) => new Entry(
userName: json["userName"] == null ? null : json["userName"],
eloRating: json["eloRating"] == null ? null : json["eloRating"],
profileIconId: json["profileIconId"] == null ? null : json["profileIconId"],
namePlateId: json["namePlateId"] == null ? null : json["namePlateId"],
);
Map<String, dynamic> toJson() => {
"userName": userName == null ? null : userName,
"eloRating": eloRating == null ? null : eloRating,
"profileIconId": profileIconId == null ? null : profileIconId,
"namePlateId": namePlateId == null ? null : namePlateId,
};
}
I looked at the documentation of loadMore. Since it says that the body of the function assigned to loadMore should be async, you do not need to use then:
loadMore: () async {
final result = await getLeaderBoards(str.length);
updateLeaderboardList(result);
},
loadMore: () async {
await getLeaderBoards(str.length).then((onValue) => {
updateLeaderboardList(onValue)
});
},
but putting "await" my loader waits for the function to complete before it finishes the animation.
am creating checkbox like this:
for (var a in user.poll.questions[i].options) {
children.add(
new Row(
children: [
new Checkbox(
value: checkBoxValue,
onChanged: (bool newValue) {
setState(() {
checkBoxValue = newValue;
});
}
),
new Text(a.text),
],
),
);
}
and this will create this:
Which is what i need .. but the problem is when one checked all will be updated and be checked and reverse ...
i want to check one .. and only this one will be checked and i get its value ..
how to do this? is there is checkbox id or something?
You can save selected question - something like:
Question selectedQuestion = null;
.....
for (var a in user.poll.questions[i].options) {
children.add(
new Row(
children: [
new Checkbox(
value: selectedQuestion == a,
onChanged: (bool newValue) {
setState(() {
selectedQuestion = a;
});
}
),
new Text(a.text),
],
),
);
}
UPD
For multiple checking:
1 - set list instead of one value
final List<Question> questionList = [];
2 - change CheckBox behavior
Checkbox(
value: questionList.contains(a),
onChanged: (bool newValue) {
setState(() {
newValue ? questionList.add(a) : questionList.remove(a);
});
}
)
I have a json of customers:
customer.json
[
{
"name": "Customer 1",
"id": "1"
},
{
"name": "Customer 2",
"id": "2"
},
{
"name": "Customer 3",
"id": "3"
}
]
This is the dart file using this json data:
customerslist.dart
Future Method
Future<String> loadCustomers() async{
var res = await http.get(
Uri.encodeFull(url),
headers: {"Accept": "application/json"});
return res.body;
}
Widget
widgets.add(new FutureBuilder(
future: loadCustomers(),
builder: (context, snapshot){
if(snapshot.hasData){
//get snapshot data from JSON tree
var jsondecode = json.decode(snapshot.data);
return new ListView.builder(
shrinkWrap: true,
itemCount: jsondecode.length,
itemBuilder: (context, index){
String name = jsondecode[index]["name"];
String id = jsondecode[index]["id"];
if(name == "Customer 2"){
return new Column(
children: <Widget>[
new ListTile(
title: new Text("Name"),
trailing: new Text(name),
),
new ListTile(
title: new Text("ID"),
trailing: new Text(id),
)
],
);
}
});
}else{
return new Center(
child: new CircularProgressIndicator(),
);
}
}));
What I am trying to do is bringing out the values if the name matches as you can see from if(name == "Customer 2").
But the problem I am having is:
When i change the if statement to if(name == "Customer 1"), the output is as you would expect:
But if i were to change it to if(name == "Customer 2"), the output turns out blank as such:
Could someone explain why is it turning out like this or is there another method I could go about doing to fix this issue?
I think it's because Column widget is taking full height. You could try using the minimum height for your column and add else condition.
if(name == "Customer 2"){
return new Column(
children: <Widget>[
new ListTile(
title: new Text("Name"),
trailing: new Text(name),
),
new ListTile(
title: new Text("ID"),
trailing: new Text(id),
)
],
);
} else {
return new Container();
}
When you use 'Customer 1' it works fine because your first element is 'Customer 1' , probably you have an error on your console because for other items there are no returning widgets.
In the case of 'Customer 2' the first item is not returned, so check your console log.
itemBuilder expects you return a widget for all the cases.
You just convert the var string to list
var jsondecode = json.decode(snapshot.data);
List list = jsondecode;
String name = list[index]["name"];
String id = list[index]["id"];
then pass the list to get the index may be this will work bocasue the var type may be get the first json string only
enter code here
I have a JSON app calling users but I am having problems dealing with null Maps.
This is the Json file.
MemberInfo.json
{
"Dependents": [
{
"Name": "Kim",
"Relationship": "Parent",
"Entitlements": {
"GP": {
"Entitlement": "10000",
"Utilisation": "500",
"Balance": "9500"
},
"OPS": {
"Entitlement": "10000",
"Utilisation": "500",
"Balance": "9500"
},
"IP": {
"Entitlement": "50000",
"Utilisation": "17000",
"Balance": "33000"
}
}
},
{
"Name": "Tim",
"Relationship": "Spouse",
"Entitlements": {
"GP": {
"Entitlement": "10000",
"Utilisation": "500",
"Balance": "9500"
},
"OPS": {
"Entitlement": "10000",
"Utilisation": "500",
"Balance": "9500"
},
"IP": {
}
}
}
]
}
And the dart file is as follows:
import 'package:flutter/material.dart';
import 'dart:convert';
import 'dart:async';
import 'package:http/http.dart' as http;
final String url = "http://crm.emastpa.com.my/MemberInfo.json";
final String url2 = "http://crm.emastpa.com.my/MemberInfo2.json";
class Dependents extends StatelessWidget {
Dependents({Key key, this.index, this.name, this.page}) : super(key:key);
final int index;
final String name;
final int page;
Future<String> jsonContent() async {
var res = await http.get(
Uri.encodeFull(url2),
headers: {"Accept": "application/json"});
return res.body;
}
#override
Widget build(BuildContext context) {
Widget fifthBody = new Container(
child: new Center(
child: new FutureBuilder<String>(
future: jsonContent(),
builder: (context, snapshot){
if(snapshot.hasData){
List<Widget> widgets = [];
//get snapshot data from JSON tree
var jsondecode = json.decode(snapshot.data);
//[]->Entitlements
var jsonEntData = jsondecode["Dependents"][index]["Entitlements"];
//Everything under Entitlements
var jsonEntDataGP = jsonEntData["GP"];
var jsonEntDataOPS = jsonEntData["OPS"];
var jsonEntDataIP = jsonEntData["IP"];
//GP Branch
var gp_ent = jsonEntDataGP["Entitlement"];
var gp_util = jsonEntDataGP["Utilisation"];
var gp_bal = jsonEntDataGP["Balance"];
//OPS branch
var ops_ent = jsonEntDataOPS["Entitlement"];
var ops_util = jsonEntDataOPS["Utilisation"];
var ops_bal = jsonEntDataOPS["Balance"];
//IP branch
var ip_ent = jsonEntDataIP["Entitlement"];
var ip_util = jsonEntDataIP["Utilisation"];
var ip_bal = jsonEntDataIP["Balance"];
jsonEntDataGP != null?
widgets.add(new ExpansionTile(
title: new Text("GP"),
children: <Widget>[
new ListTile(
title: new Text("Entitlement"),
trailing: new Text(gp_ent),
)
],
))
: new Center();
jsonEntDataOPS != null?
widgets.add(new ExpansionTile(
title: new Text("OPS"),
children: <Widget>[
new ListTile(
title: new Text("Entitlement"),
trailing: new Text(ops_ent),
)
]))
: new Center();
jsonEntDataIP != null?
widgets.add(new ExpansionTile(
title: new Text("IP"),
children: <Widget>[
new ListTile(
title: new Text("Entitlement"),
trailing: new Text(ip_ent),
)
]))
: new Center();
return new Column(
children: widgets,
);
}else if(snapshot.hasError){
return new Text(snapshot.error);
}
//loading the page
return new Center(
child: new CircularProgressIndicator(),
);
}),
),
);
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text("$name + index: $index"),
),
body: fifthBody
),
);
}
}
This is the error I am getting:
I/flutter ( 8842): The following assertion was thrown building FutureBuilder<String>(dirty, state:
I/flutter ( 8842): _FutureBuilderState<String>#f9262):
I/flutter ( 8842): 'package:flutter/src/widgets/text.dart': Failed assertion: line 230 pos 15: 'data != null': is not
I/flutter ( 8842): true.
I am trying to build a list of the user's entitlements but I am not so sure as to how to deal with a null key such as "IP". The app works as long as the key has values inside but will return the exception if the key is null.
How should I deal with this problem?
IP is empty JsonObject
"IP": {},
So when you try to access data of IP object it returns null & passing that to textview later gives data is null error.
You are trying to get data from json object without keys as
var ip_ent = jsonEntDataIP["Entitlement"];
And using later as
new Text(ip_ent)
To overcome this you need to assign default value in case of variable is null or can hide that view as your requirement.
To assign default value in case of null, please do this.
var ip_ent = jsonEntDataIP["Entitlement"] ?? '';
var jsonEntDataIP = jsonEntData["IP"] ?? '';
I'm familiar with form validation using a TextFormField in Flutter, but is it possible to integrate a DropdownButton into a Form and require one of its value be selected before submission?
Basically, integrate DropdownButton validation into this basic Flutter validation example:
https://flutter.io/cookbook/forms/validation/
Dart Package have already the widget DropdownButtonFormField for this. Here is an example of how to use it:
List<String> typeNeg = [
"One",
"Two",
"Three",];
String dropdownValue = "One";
DropdownButtonFormField<String>(
value: dropdownValue,
hint: Text("Type of business"),
onChanged: (String newValue) {
setState(() {
dropdownValue = newValue;
});
},
validator: (String value) {
if (value?.isEmpty ?? true) {
return 'Please enter a valid type of business';
}
},
items: typeNeg
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
onSaved: (val) => setState(() => _user.typeNeg = val),
),
The user model is as follows:
class User {
int id;
String name;
String email;
String typeNeg;
User({this.id, this.name, this.email, this.typeNeg});
factory User.fromJson(Map<String, dynamic> parsedJson) {
return User(
id: parsedJson["id"],
name: parsedJson["name"] as String,
email: parsedJson["email"] as String,
typeNeg: parsedJson["typeNeg"] as String,
);
}
save(){
print("User saved");
}
}
To try the validator option change String dropdownValue = "One"; to String dropdownValue = null;
From text_form_field.dart file in Flutter's source code you can see that TextFormField is no more than a FormField emitting a TextField widget in its builder callback. You can write your own DropdownFormField using a similar pattern. Here's mine:
import 'package:flutter/material.dart';
class DropdownFormField<T> extends FormField<T> {
DropdownFormField({
Key key,
InputDecoration decoration,
T initialValue,
List<DropdownMenuItem<T>> items,
bool autovalidate = false,
FormFieldSetter<T> onSaved,
FormFieldValidator<T> validator,
}) : super(
key: key,
onSaved: onSaved,
validator: validator,
autovalidate: autovalidate,
initialValue: items.contains(initialValue) ? initialValue : null,
builder: (FormFieldState<T> field) {
final InputDecoration effectiveDecoration = (decoration ?? const InputDecoration())
.applyDefaults(Theme.of(field.context).inputDecorationTheme);
return InputDecorator(
decoration:
effectiveDecoration.copyWith(errorText: field.hasError ? field.errorText : null),
isEmpty: field.value == '' || field.value == null,
child: DropdownButtonHideUnderline(
child: DropdownButton<T>(
value: field.value,
isDense: true,
onChanged: field.didChange,
items: items.toList(),
),
),
);
},
);
}
The key is to bind DropdownButton's onChanged to field.didChange. Usage is pretty straightforward:
DropdownFormField<String>(
validator: (value) {
if (value == null) {
return 'Required';
}
},
onSaved: (value) {
// ...
},
decoration: InputDecoration(
border: UnderlineInputBorder(),
filled: true,
labelText: 'Demo',
),
initialValue: null,
items: [
DropdownMenuItem<String>(
value: '1',
child: Text('1'),
),
DropdownMenuItem<String>(
value: '2',
child: Text('2'),
)
],
),
I got the idea from this site. The difference is that my version of DropdownFormField is closer to Flutter's native implementation (which extends TextFormField instead of wrapping it inside a StatefulWidget).