How can I send the data I get from the API to the other pages? Before using getx i was sending with "widget.bla bla" but now i don't know how can i send it.
class HomePage extends StatelessWidget {
final AllCoinController allCoinController = Get.put(AllCoinController());
#override
Widget build(BuildContext context) {
return Scaffold(
body: Obx(
() => ListView.builder(
scrollDirection: Axis.vertical,
itemCount: allCoinController.coinList.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: GestureDetector(
onTap: () {
Get.to(CoinContent());
},
child: Container(
color: Colors.grey[700],
width: 150,
child: Row(
children: [
SizedBox(
width: 50,
height: 50,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Image.network(
allCoinController.coinList[index].image),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(allCoinController.coinList[index].name),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(allCoinController.coinList[index].symbol),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(allCoinController
.coinList[index].currentPrice
.toString()),
),
],
),
),
),
);
},
),
),
);
}
}
The page I want to send the data to:
class CoinContent extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text("coin name"),
),
body: Obx(
() => Center(
child: Column(
children: [
Text("coin data 1"),
Text("coin data 2"),
Text("coin data 3"),
Text("coin data 4"),
Text("coin data 5"),
Text("coin data 6"),
],
),
),
),
);
}
}
And my last question codes are not found automatically when using Getx. Example:
Text(allCoinController.coinList[index].currentPrice.toString()),
I get the same data without using getx and there was no problem. But when using Getx the "currentPrice" code is not automatically found and does not appear. I need to copy the code to write.
My controller:
import 'dart:async';
import 'package:coin_finder/models/btc_eth_bnb_model.dart';
import 'package:get/get.dart';
import 'package:http/http.dart' as http;
class AllCoinController extends GetxController {
var coinList = [].obs;
final url = Uri.parse("api url")
Future callAllCoins() async {
try {
final response = await http.get(url);
if (response.statusCode == 200) {
List<dynamic> values = [];
values = allCoinsFromJson(response.body);
coinList.assignAll(values);
if (values.length > 0) {
for (int i = 0; i < values.length; i++) {
if (values[i] != null) {
coinList.add(values[i]);
}
}
}
return coinList;
} else {
print(response.statusCode);
}
} catch (e) {
print(e.toString());
}
}
#override
void onInit() {
callAllCoins();
Timer.periodic(Duration(minutes: 5), (timer) => callAllCoins());
super.onInit();
}
}
Model:
import 'dart:convert';
List<AllCoins> allCoinsFromJson(String str) =>
List<AllCoins>.from(json.decode(str).map((x) => AllCoins.fromJson(x)));
String allCoinsToJson(List<AllCoins> data) =>
json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class AllCoins {
AllCoins({
required this.symbol,
required this.name,
required this.image,
required this.currentPrice,
});
String symbol;
String name;
String image;
num currentPrice;
factory AllCoins.fromJson(Map<String, dynamic> json) => AllCoins(
symbol: json["symbol"],
name: json["name"],
image: json["image"],
currentPrice: json["current_price"],
);
Map<String, dynamic> toJson() => {
"symbol": symbol,
"name": name,
"image": image,
"current_price": currentPrice,
};
}
Dart version: sdk: ">=2.12.0 <3.0.0"
in home page Get.to(CoinContent(),arguments:
allCoinController.coinList[index] )
//
class CoinContent extends StatelessWidget {
#override
Widget build(BuildContext context) {
final data = ModalRoute.of(context)!.settings.arguments as
AllCoins;
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text("coin name"),
),
body:Center(
child: Column(
children: [
Text("${data.name}"),
],
),
),
);
}
}
in this case we aren't using agrs in constructor
instead of routing like that Get.to(MyScreen());
you should use Get.to(MyScreen(), arguments: [1,2,3]);
you can access them by using Get.arguments it should return a List
Note: The arguments will not change while using the app until you override it by routing to another screen with args
Related
I am trying to implement algolia search in flutter with filters.
I found an article on the algolia website which I followed to a tee to get to this stage in the implementation but I am getting this error and have no clue what to do about it:
lib/services/search.dart:194:52: Error: Member not found: 'fromResponse'.
_assetsSearcher.responses.map(SearchMetadata.fromResponse);
^^^^^^^^^^^^
lib/services/search.dart:200:46: Error: Member not found: 'fromResponse'.
_assetsSearcher.responses.map(HitsPage.fromResponse);
^^^^^^^^^^^^
This is my code:
class SearchMetadata {
final int nbHits;
const SearchMetadata(this.nbHits);
factory SearchMetadata.fromResponse(SearchResponse response) =>
SearchMetadata(response.nbHits);
}
class Asset {
final String code;
final String name;
final String desc;
final String loc;
final String img;
Asset(this.code, this.name, this.desc, this.loc, this.img);
static Asset fromJson(Map<String, dynamic> json) {
return Asset(
json['name'], json['desc'], json['desc'], json['loc'], json['img']);
}
}
class HitsPage {
const HitsPage(this.assets, this.pageKey, this.nextPageKey);
final List<Asset> assets;
final int pageKey;
final int? nextPageKey;
factory HitsPage.fromResponse(SearchResponse response) {
final assets = response.hits.map(Asset.fromJson).toList();
final isLastPage = response.page >= response.nbPages;
final nextPageKey = isLastPage ? null : response.page + 1;
return HitsPage(assets, response.page, nextPageKey);
}
}
class AlgoliaSearchFilters extends StatefulWidget {
const AlgoliaSearchFilters({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<AlgoliaSearchFilters> createState() => _AlgoliaSearchFiltersState();
}
class _AlgoliaSearchFiltersState extends State<AlgoliaSearchFilters> {
final _searchTextController = TextEditingController();
final _assetsSearcher = HitsSearcher(
applicationID: 'OPN6AROJK6',
apiKey: '0ba458475d25b7e2069d700c32e42f29',
indexName: 'asset');
Stream<SearchMetadata> get _searchMetadata =>
_assetsSearcher.responses.map(SearchMetadata.fromResponse);
final PagingController<int, Asset> _pagingController =
PagingController(firstPageKey: 0);
Stream<HitsPage> get _searchPage =>
_assetsSearcher.responses.map(HitsPage.fromResponse);
final GlobalKey<ScaffoldState> _mainScaffoldKey = GlobalKey();
final _filterState = FilterState();
late final _facetList = FacetList(
searcher: _assetsSearcher, filterState: _filterState, attribute: 'loc');
#override
void initState() {
super.initState();
_searchTextController
.addListener(() => _assetsSearcher.query(_searchTextController.text));
_searchPage.listen((page) {
if (page.pageKey == 0) {
_pagingController.refresh();
}
_pagingController.appendPage(page.assets, page.nextPageKey);
}).onError((error) => _pagingController.error = error);
_pagingController.addPageRequestListener((pageKey) =>
_assetsSearcher.applyState((state) => state.copyWith(page: pageKey)));
_assetsSearcher.connectFilterState(_filterState);
_filterState.filters.listen((_) => _pagingController.refresh());
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: _mainScaffoldKey,
appBar: AppBar(
backgroundColor: Colors.blue[900],
title: const Text("Algolia Flutter Search"),
actions: [
IconButton(
onPressed: () => _mainScaffoldKey.currentState?.openEndDrawer(),
icon: const Icon(Icons.filter_list_sharp))
],
),
endDrawer: Drawer(
child: _filters(context),
),
body: Center(
child: Column(
children: <Widget>[
SizedBox(
height: 44,
child: TextField(
controller: _searchTextController,
decoration: const InputDecoration(
border: InputBorder.none,
hintText: 'Enter a search term',
prefixIcon: Icon(Icons.search),
),
)),
StreamBuilder<SearchMetadata>(
stream: _searchMetadata,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const SizedBox.shrink();
}
return Padding(
padding: const EdgeInsets.all(8.0),
child: Text('${snapshot.data!.nbHits} hits'),
);
},
),
Expanded(
child: _hits(context),
)
],
),
),
);
}
Widget _hits(BuildContext context) => PagedListView<int, Asset>(
pagingController: _pagingController,
builderDelegate: PagedChildBuilderDelegate<Asset>(
noItemsFoundIndicatorBuilder: (_) => const Center(
child: Text('No results found'),
),
itemBuilder: (_, item, __) => Container(
color: Colors.white,
height: 80,
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
SizedBox(width: 50, child: Image.network(item.img)),
const SizedBox(width: 20),
Expanded(child: Text(item.name))
],
),
)));
Widget _filters(BuildContext context) => Scaffold(
appBar: AppBar(
backgroundColor: Colors.blue[900],
title: const Text('Filters'),
),
body: StreamBuilder<List<SelectableItem<Facet>>>(
stream: _facetList.facets,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const SizedBox.shrink();
}
final selectableFacets = snapshot.data!;
return ListView.builder(
padding: const EdgeInsets.all(8.0),
itemCount: selectableFacets.length,
itemBuilder: (_, index) {
final selectableFacet = selectableFacets[index];
return CheckboxListTile(
value: selectableFacet.isSelected,
title: Text(
'${selectableFacet.item.value} (${selectableFacet.item.count})'),
onChanged: (_) {
_facetList.toggle(selectableFacet.item.value);
},
);
});
}),
);
#override
void dispose() {
_searchTextController.dispose();
_assetsSearcher.dispose();
_pagingController.dispose();
_filterState.dispose();
_facetList.dispose();
super.dispose();
}
}
It shows up as an error only when I run my program
Please let me know of any fixes
Thanks!
I had the same issue after updating flutter. Try run
flutter pub upgrade
to upgrade all your dependencies as this is caused by a transitive dependencies.
I want to know how to send data to another class.
Imported from gRPC data,List convert to String and send HomePage Class
Finally, I want to express the data that was imported in a chat message.
I copied it from YouTube, so I don't have enough code.
class Client{
var ro;
var de = Int64.parseRadix('24A16057F615', 16);
var gw;
List<int> da;
ExProtoClient stub = ExProtoClient(
ClientChannel('172.22.144.1', port: 5054,
options: const ChannelOptions(
credentials: ChannelCredentials.insecure())
));
final box = new ExMessage();
Future<ExMessage> sendMessage() async {
await stub.exClientstream(box
..gwId = 0x51894B30
..route = 1835139072
..dataUnit = [0x01, 0x03, 0x00, 0x65, 0x00, 0x28, 0xAD, 0xDE]
..deviceId = de
);
return(box);
}
Future<ExMessage> receiveMessage() async {
var request = ExMessage();
await stub.exServerstream(request).listen((response) {
if (response.dataUnit != null){
ro = response.route;
da = response.dataUnit;
de = response.deviceId;
gw = response.gwId;
rxMtu(ro, gw, de, da);
}
else{
print('wating');
}
});
}
void rxMtu(int gwGroup, int gwId, Int64 deviceId, List payload)
{
String data = payload.join('-');
print(data);
}
}
It's a Class to send data
I want to send variable data from rxMtu.
class HomePage extends StatefulWidget {
#override
HomePageState createState() => new HomePageState();
static HomePageState of(BuildContext context) =>
context.findAncestorStateOfType<HomePageState>();
}
class HomePageState extends State<HomePage> {
var client = Client();
//TextEditor
TextEditingController _TextEditingController = TextEditingController();
List<ChatMessage> _chats = [];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("gRPC on Flutter"),
),
body: Column(
children: [
Expanded(
child: ListView.builder(
reverse: true,
itemBuilder: (context, index){
return _chats[index];
},
itemCount: _chats.length,
)),
Container(
padding: EdgeInsets.only(left: 8.0, right: 8.0),
child: Row(
children: [
Expanded(
child: TextField(
controller: _TextEditingController,
decoration: InputDecoration(hintText: "Send Message"),
onSubmitted: (String text){
_handleSubmitted(text);
},
),
),
SizedBox(
width: 8.0,
),
FlatButton(onPressed: (client.sendMessage)
, child: Text("Send"), color: Colors.greenAccent,),
],
),
),
],
),
);
}
void _handleSubmitted(String text){
Logger().d(text);
_TextEditingController.clear();
ChatMessage newChat = ChatMessage(text);
setState(() {
_chats.insert(0, newChat);
});
}
}
It's Class to receive data
I want to send the data to _handleSubmitted function
class ChatMessage extends StatelessWidget {
final String txt;
const ChatMessage(this.txt,{Key key,}) : super(key: key);
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8),
child: Row(children: [
CircleAvatar(
backgroundColor: Colors.blueGrey,
child: Text("Data"),
),
SizedBox(width: 16,),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Payload",
style: TextStyle(fontWeight:FontWeight.bold ),
),
Text(txt),
],
)
)
],
),
);
}
}
this is chatMessage Class
Thank you for reading it.
I am not sure about your problem, but if you need a class that shares data and methods with other classes or widgets, you need to use state management, for example, provider, Bloc or etc.
I suggest to use provider as follow:
first add it to you pubspec.yaml file (dependency section)
provider: ^4.0.1
create your class
class MessageModel extends ChangeNotifier {
// define your variables and methods
}
add the provider to your main page
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => MessageModel(),
child: MyApp(),
),
);
}
use each method or variable, everwhere you want
Provider.of<MessageModel>(context, listen: false).NamOfYourMehto();
I hope it could be helpful.
I'm new to Flutter and Dart. I made a simple Todos app with Provider package and Consumer. I'm trying to implement a process that uses flutter_secure_storage package to read and write the list data on the device.
But I don't know how to implement it.
Checkbox widget requires a bool, while secure_storage requires the type of String. Therefore, it is necessary to convert both types. This also confuses me a little.
The code is below.
I would be happy if you could give me some advice.
main.dart
// Todos app example
import 'package:consumer_samp/list_model.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: ChangeNotifierProvider<ListModel>(
create: (context) => ListModel(),
child: MyHomePage('Todos app example'),
),
);
}
}
class MyHomePage extends StatelessWidget {
MyHomePage(this.title);
final String title;
final TextEditingController eCtrl = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: SingleChildScrollView(
child: Column(
children: <Widget>[
Container(
padding: EdgeInsets.all(10),
child: Row(
children: <Widget>[
Expanded(
child: TextField(
decoration: InputDecoration(
border: InputBorder.none,
hintText: 'Enter your ToDo item',
),
controller: eCtrl,
),
),
Consumer<ListModel>(
builder: (_, listModel, __) => FlatButton(
child: Text('Add'),
onPressed: () {
if (eCtrl.text != '') {
Map<String, dynamic> item = {
'value': false,
'text': eCtrl.text
};
listModel.addItem(item);
eCtrl.clear();
}
},
),
),
],
),
),
Center(
child: Consumer<ListModel>(builder: (_, listModel, __) {
var items = listModel.getItems;
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
height: 300,
child: ListView.builder(
itemCount: items.length,
itemBuilder: (BuildContext context, int idx) =>
Container(
padding: const EdgeInsets.all(5),
color: Colors.green,
child: Row(
children: <Widget>[
Checkbox(
value: items[idx]['value'],
onChanged: (val) {
listModel.toggleValue(idx, val);
}),
Text(
items[idx]['text'],
style: TextStyle(
fontSize: 21, color: Colors.white),
),
],
),
),
),
),
],
);
}),
),
],
),
),
);
}
}
list_model.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
class ListModel with ChangeNotifier {
List<Map<String, dynamic>> _items = [];
List<Map<String, dynamic>> get getItems => _items;
void addItem(Map<String, dynamic> item) {
_items.add(item);
notifyListeners();
}
void toggleValue(int idx, bool val) {
_items[idx]['value'] = val;
notifyListeners();
}
}
You can copy paste run full code below
You can use class TodoItem and save/load with JSON String
Provider logic for secure storage is in full code, too long to describe detail
code snippet
List<TodoItem> todoItemFromJson(String str) =>
List<TodoItem>.from(json.decode(str).map((x) => TodoItem.fromJson(x)));
String todoItemToJson(List<TodoItem> data) =>
json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class TodoItem {
TodoItem({
this.item,
this.checked,
});
String item;
bool checked;
factory TodoItem.fromJson(Map<String, dynamic> json) => TodoItem(
item: json["item"],
checked: json["checked"],
);
Map<String, dynamic> toJson() => {
"item": item,
"checked": checked,
};
}
working demo
full code
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'dart:convert';
List<TodoItem> todoItemFromJson(String str) =>
List<TodoItem>.from(json.decode(str).map((x) => TodoItem.fromJson(x)));
String todoItemToJson(List<TodoItem> data) =>
json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class TodoItem {
TodoItem({
this.item,
this.checked,
});
String item;
bool checked;
factory TodoItem.fromJson(Map<String, dynamic> json) => TodoItem(
item: json["item"],
checked: json["checked"],
);
Map<String, dynamic> toJson() => {
"item": item,
"checked": checked,
};
}
class ListModel with ChangeNotifier {
FlutterSecureStorage _storage;
List<TodoItem> _items = [];
List<TodoItem> get getItems => _items;
initilaize() async {
print("initialize");
String jsonString = await _storage.read(key: "todo");
if (jsonString != null) {
_items = todoItemFromJson(jsonString);
notifyListeners();
}
}
ListModel.init(FlutterSecureStorage storage) {
print("init");
_storage = storage;
initilaize();
}
void update(FlutterSecureStorage storage) {
print("update");
_storage = storage;
}
void addItem(TodoItem item) {
_items.add(item);
notifyListeners();
}
void toggleValue(int idx, bool val) {
_items[idx].checked = val;
notifyListeners();
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: MultiProvider(
providers: [
Provider<FlutterSecureStorage>(create: (_) => FlutterSecureStorage()),
ChangeNotifierProxyProvider<FlutterSecureStorage, ListModel>(
create: (_) {
return ListModel.init(
Provider.of<FlutterSecureStorage>(_, listen: false));
},
update: (_, storage, listModel) => listModel..update(storage),
),
],
child: MyHomePage('Todos app example'),
),
);
}
}
class MyHomePage extends StatelessWidget {
MyHomePage(this.title);
final String title;
final TextEditingController eCtrl = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
actions: <Widget>[
Consumer2<ListModel, FlutterSecureStorage>(
builder: (_, listModel, storage, __) => IconButton(
icon: Icon(Icons.save),
onPressed: () async {
await storage.write(
key: "todo", value: todoItemToJson(listModel._items));
print("save done");
},
))
],
),
body: SingleChildScrollView(
child: Column(
children: <Widget>[
Container(
padding: EdgeInsets.all(10),
child: Row(
children: <Widget>[
Expanded(
child: TextField(
decoration: InputDecoration(
border: InputBorder.none,
hintText: 'Enter your ToDo item',
),
controller: eCtrl,
),
),
Consumer<ListModel>(
builder: (_, listModel, __) => FlatButton(
child: Text('Add'),
onPressed: () {
if (eCtrl.text != '') {
Map<String, dynamic> item = {
'value': false,
'text': eCtrl.text
};
listModel.addItem(
TodoItem(item: eCtrl.text, checked: false));
eCtrl.clear();
}
},
),
),
],
),
),
Center(
child: Consumer<ListModel>(builder: (_, listModel, __) {
var items = listModel.getItems;
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
height: 300,
child: ListView.builder(
itemCount: items.length,
itemBuilder: (BuildContext context, int idx) =>
Container(
padding: const EdgeInsets.all(5),
color: Colors.green,
child: Row(
children: <Widget>[
Checkbox(
value: items[idx].checked,
onChanged: (val) {
listModel.toggleValue(idx, val);
}),
Text(
items[idx].item,
style: TextStyle(
fontSize: 21, color: Colors.white),
),
],
),
),
),
),
],
);
}),
),
],
),
),
);
}
}
As mentioned in the readme files by the following link
https://pub.dev/packages/flutter_secure_storage
The following code instantiate the storage, and the following code's must be in a async method as this returns a future.
final storage = new FlutterSecureStorage();
And you can pass the _items list as a value and you can set some key name
await storage.write(key: key, value: _items);
And then you could get that value by using the key name (which is set while storing)
List<Map<String, dynamic>> _value = await storage.read(key: key);
And then you could map the _value you get from storage and you can store it in _items. And then you could do various operations and queries inside your app now with all data you have.
Make a note! I haven't tried this approach in my code base. Just I said what I thought. Please try this in your code and comment me please.
The following code executes correctly use this in model, for storing data:
Future<void> _storingData() async {
final storage = new FlutterSecureStorage();
for (int i = 0; i < _items.length; i++) {
await storage
.write(key: _items[i]['text'], value: "${_items[i]['value']}")
.then((value) => print("success"));
}
}
For retrieving data:
Future<void> retrivingData() async {
final storage = new FlutterSecureStorage();
Map<String, String> allValues = await storage.readAll();
print(allValues);
allValues.forEach((key, value) {
bool val = bool.fromEnvironment(value, defaultValue: false);
Map<String, dynamic> item = {'value': val, 'text': key};
addItem(item);
});
}
and finally I stored all values again to a list.
You should do some changes in your code in main.dart according to the above methods based on your usage.
Displaying the data from my API based on the Dropdown selected value. I want to display on the same page. The data from the server(response) is displaying on the console. But still, this data is not displaying.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:http/http.dart' as http;
//import 'package:json_parsing_example/model2.dart';
//import 'package:json_parsing_example/models.dart'
List<YouModel> youModelFromJson(String str) => List<YouModel>.from(json.decode(str).map((x) => YouModel.fromJson(x)));
String youModelToJson(List<YouModel> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class YouModel {
String columnName;
YouModel({
this.columnName,
});
factory YouModel.fromJson(Map<String, dynamic> json) => YouModel(
columnName: json["column_name"],
);
Map<String, dynamic> toJson() => {
"column_name": columnName,
};
}
UserModel userModelFromJson(String str) => UserModel.fromJson(json.decode(str));
String userModelToJson(UserModel data) => json.encode(data.toJson());
class UserModel {
String username;
String name;
UserModel({
this.username,
this.name,
});
factory UserModel.fromJson(Map<String, dynamic> json) => UserModel(
username: json["username"],
name: json["Name"],
);
Map<String, dynamic> toJson() => {
"username": username,
"Name": name,
};
}
class Addoffers2 extends StatefulWidget {
#override
State<StatefulWidget> createState() => _Addoffers2State();
}
class _Addoffers2State extends State<Addoffers2> {
List<String> _companies = [];
bool _isLoading = false;
String _selectedCompany;
#override
void initState() {
super.initState();
_selectedCompany=null;
_getcompanylist();
}
Future<String> loadFromAssets() async {
return await rootBundle.loadString('json/parse.json');
}
_getcompanylist() async {
setState(() {
_isLoading = true;
});
print("getting..");
final responseStr =
await http.get('http://10.0.2.2/Flutter/GetCompanieslist.php');
//String responseStr = await loadFromAssets();
final listData = youModelFromJson(responseStr.body);
for(int i=0;i<listData.length;i++)
{
print('this is the list :'+listData[i].columnName);
// _companies.add(listData[i].columnName);
}
// above method is the standard method to get creating a model class and then get the list of strings
// I have just shown you but example is according to you code .
// this above loadFromAssets is that you hit the api and you get the json string response
// i have created a dummy json file where i can the String.
// Else everything is the same as below you just have to pass the response.body to the json.decode method.
var jsonData = json.decode(responseStr.body);
for (var u in jsonData) {
_companies.add(u.toString().substring(14, u.toString().length - 1));
}
for (int i = 0; i < _companies.length; i++) {
print(_companies[i].toString());
}
setState(() {
_isLoading = false;
});
}
#override
Widget build(BuildContext context) {
//double width = MediaQuery.of(context).size.width;
//double height = MediaQuery.of(context).size.height;
return MaterialApp(
//color: Colors.red,
home: Scaffold(
backgroundColor: Colors.red,
appBar: AppBar(
backgroundColor: Theme.of(context).backgroundColor,
title: Text("Add.."),
),
body: Container(
color: Colors.blue,
// just put your height i have modified it replace it by height / 8
child: _isLoading
? CircularProgressIndicator()
: Center(
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
//MainAxisAlignment: MainAxisAlignment.spaceBetween,
Text('Choose..'),
DropdownButtonHideUnderline(
child: DropdownButton(
// hint: Text('Choose Company'), // Not necessary for Option 1
value: _selectedCompany,
onChanged: (newValue) {
setState(() {
_selectedCompany = newValue;
// here i have taken the boolen variable to show and hide the list if you have not seleted the value from the dropdown the it will show the text and if selected the it will show you the list.
});
print(_selectedCompany);
},
items: _companies.map((company) {
return DropdownMenuItem(
child: new Text(company.toString()),
value: company,
);
}).toList(),
),
),
],
),
),
),
// this is to to check for the initial if string is null then show the text widget.
// else if the value is selected then it will show the listview
_selectedCompany == null
? Text('Select the dropdown value for list to appear.')// sample text you can modify
: Padding(
padding: const EdgeInsets.all(0.0),
child: Container(
height: 100,
color: Theme.of(context).backgroundColor,
child: new FutureBuilder(
future: _getUsers(
_selectedCompany), // a Future<String> or null
builder: (BuildContext context,
AsyncSnapshot snapshot) {
if (snapshot.connectionState ==
ConnectionState.waiting) {
return Container(
child: Center(
child: new CircularProgressIndicator(
backgroundColor: Colors.white,
),
));
}
if (snapshot.hasError) {
return Center(
child: new Text(
'Error ${snapshot.error}'),
);
} else {
return Center(
child: Padding(
padding: const EdgeInsets.fromLTRB(
5.0, 8.0, 5.0, 8.0),
child: ListView.builder(
itemCount: snapshot.data.length,
itemBuilder:
(BuildContext context,
int index) {
List<UserModel> user =
snapshot.data;
var username =
user[index].username;
var stuname =
user[index].name;
print(
'This is the user name :$username');
print(
'This is the name : $stuname');
//var title=snapshot.data[index]["Title"];
// new Text(parsedDate.toString());
return StudentList2(
regdNo: username,
name: stuname);
}),
),
);
}
}),
),
),
],
)),
)),
);
}
}
Future<String> loadFromAssets2() async {
return await rootBundle.loadString('json/parse2.json');
}
// the above method is just for the sample purpose where you get you json String after hitting the api call for _getUsers method
Future<List<UserModel>> _getUsers(String selectedcompany) async {
// here you call you api and you get the response
var url = 'https://10.0.2.2/Flutter/getstudentdata.php;
var data = { 'company': selectedcompany};
// Starting Web Call with data.
var response = await http.post(url, body: json.encode(data));
print(response.body);
//String responseStr = await loadFromAssets2();
final userModel = userModelFromJson(response.body);
// I have just made the model class for where fromt he below you get the complete object and then added to the list and returned.
List<UserModel> users = [];
users.add(userModel);
print('This is the name : ${users[0].name}'); // Even this also not getting printed
return users;
}
class StudentList2 extends StatefulWidget {
final regdNo;
final name;
const StudentList2({
Key key,
this.regdNo,
this.name,
}) : super(key: key);
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<StudentList2> {
bool visible = false;
#override
Widget build(BuildContext context) {
print(widget.regdNo.toString());
return Padding(
padding: const EdgeInsets.symmetric(vertical: 5.0),
child: new Card(
color: Theme.of(context).primaryColor,
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.fromLTRB(8.0, 8.0, 8.0, 2.0),
child: Container(
child: new Text(
widget.regdNo.toUpperCase(),
style: TextStyle(
color: Colors.yellowAccent,
fontWeight: FontWeight.bold,
fontSize: 15.0,
),
),
),
),
ListTile(
title: new Text(
widget.regdNo,
style: TextStyle(
color: Colors.black,
fontSize: 14.0,
),
),
subtitle: new Text(
(widget.name),
style: TextStyle(
color: Colors.black,
fontSize: 15.0,
),
),
),
//
],
)),
);
}
}
I am able to retrieve the data from the server and print it on the console. Still, the data is not displaying. I do not know where I did the mistake.
So I have completely updated the answer and there are many things that you don't follow according to the global standard.
So I have listed some of the key things that you should follow :
Following is you company list json :
[
{
"column_name": "ABC"
},
{
"column_name": "XYZ"
}
]
Following is the get user json that you will get :
{"username":"1111","Name":"ABC" }
And Later the model class I have create accordingly to the json that you provided and then you can create your own based in the added json.
There are Two model classes that I have created :
First model class is for the company :
// To parse this JSON data, do
//
// final youModel = youModelFromJson(jsonString);
import 'dart:convert';
List<YouModel> youModelFromJson(String str) => List<YouModel>.from(json.decode(str).map((x) => YouModel.fromJson(x)));
String youModelToJson(List<YouModel> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class YouModel {
String columnName;
YouModel({
this.columnName,
});
factory YouModel.fromJson(Map<String, dynamic> json) => YouModel(
columnName: json["column_name"],
);
Map<String, dynamic> toJson() => {
"column_name": columnName,
};
}
second mode class is for the user :
// To parse this JSON data, do
//
// final userModel = userModelFromJson(jsonString);
import 'dart:convert';
UserModel userModelFromJson(String str) => UserModel.fromJson(json.decode(str));
String userModelToJson(UserModel data) => json.encode(data.toJson());
class UserModel {
String username;
String name;
UserModel({
this.username,
this.name,
});
factory UserModel.fromJson(Map<String, dynamic> json) => UserModel(
username: json["username"],
name: json["Name"],
);
Map<String, dynamic> toJson() => {
"username": username,
"Name": name,
};
}
Below is the main ui file just Check the comments that I have made so that it will be helpful for you .
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:json_parsing_example/model2.dart';
import 'package:json_parsing_example/models.dart';
void main() => runApp(Addoffers());
class Addoffers extends StatefulWidget {
#override
State<StatefulWidget> createState() => _AddoffersState();
}
class _AddoffersState extends State<Addoffers> {
List<String> _companies = [];
bool _isLoading = false;
String _selectedCompany;
#override
void initState() {
super.initState();
_selectedCompany=null;
_getcompanylist();
}
Future<String> loadFromAssets() async {
return await rootBundle.loadString('json/parse.json');
}
_getcompanylist() async {
setState(() {
_isLoading = true;
});
print("getting..");
/* final response =
await http.get('http://10.0.2.2/Flutter/GetCompanieslist.php'); */
String responseStr = await loadFromAssets();
final listData = youModelFromJson(responseStr);
for(int i=0;i<listData.length;i++)
{
print('this is the list :'+listData[i].columnName);
// _companies.add(listData[i].columnName);
}
// above method is the standard method to get creating a model class and then get the list of strings
// I have just shown you but example is according to you code .
// this above loadFromAssets is that you hit the api and you get the json string response
// i have created a dummy json file where i can the String.
// Else everything is the same as below you just have to pass the response.body to the json.decode method.
var jsonData = json.decode(responseStr);
for (var u in jsonData) {
_companies.add(u.toString().substring(14, u.toString().length - 1));
}
for (int i = 0; i < _companies.length; i++) {
print(_companies[i].toString());
}
setState(() {
_isLoading = false;
});
}
#override
Widget build(BuildContext context) {
//double width = MediaQuery.of(context).size.width;
//double height = MediaQuery.of(context).size.height;
return MaterialApp(
//color: Colors.red,
home: Scaffold(
backgroundColor: Colors.red,
appBar: AppBar(
backgroundColor: Theme.of(context).backgroundColor,
title: Text("Add.."),
),
body: Container(
color: Colors.blue,
// just put your height i have modified it replace it by height / 8
child: _isLoading
? CircularProgressIndicator()
: Center(
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
//MainAxisAlignment: MainAxisAlignment.spaceBetween,
Text('Choose..'),
DropdownButtonHideUnderline(
child: DropdownButton(
// hint: Text('Choose Company'), // Not necessary for Option 1
value: _selectedCompany,
onChanged: (newValue) {
setState(() {
_selectedCompany = newValue;
// here i have taken the boolen variable to show and hide the list if you have not seleted the value from the dropdown the it will show the text and if selected the it will show you the list.
});
print(_selectedCompany);
},
items: _companies.map((company) {
return DropdownMenuItem(
child: new Text(company.toString()),
value: company,
);
}).toList(),
),
),
],
),
),
),
// this is to to check for the initial if string is null then show the text widget.
// else if the value is selected then it will show the listview
_selectedCompany == null
? Text('Select the dropdown value for list to appear.')// sample text you can modify
: Padding(
padding: const EdgeInsets.all(0.0),
child: Container(
height: 100,
color: Theme.of(context).backgroundColor,
child: new FutureBuilder(
future: _getUsers(
_selectedCompany), // a Future<String> or null
builder: (BuildContext context,
AsyncSnapshot snapshot) {
if (snapshot.connectionState ==
ConnectionState.waiting) {
return Container(
child: Center(
child: new CircularProgressIndicator(
backgroundColor: Colors.white,
),
));
}
if (snapshot.hasError) {
return Center(
child: new Text(
'Error ${snapshot.error}'),
);
} else {
return Center(
child: Padding(
padding: const EdgeInsets.fromLTRB(
5.0, 8.0, 5.0, 8.0),
child: ListView.builder(
itemCount: snapshot.data.length,
itemBuilder:
(BuildContext context,
int index) {
List<UserModel> user =
snapshot.data;
var username =
user[index].username;
var stuname =
user[index].name;
print(
'This is the user name :$username');
print(
'This is the name : $stuname');
//var title=snapshot.data[index]["Title"];
// new Text(parsedDate.toString());
return StudentList2(
regdNo: username,
name: stuname);
}),
),
);
}
}),
),
),
],
)),
)),
);
}
}
Future<String> loadFromAssets2() async {
return await rootBundle.loadString('json/parse2.json');
}
// the above method is just for the sample purpose where you get you json String after hitting the api call for _getUsers method
Future<List<UserModel>> _getUsers(String selectedcompany) async {
/* var data = await http.post("http://10.0.2.2/Flutter/getstdata.php", body: {
"company": selectedcompany,
//print(data.body);
}); */
// here you call you api and you get the response
String responseStr = await loadFromAssets2();
final userModel = userModelFromJson(responseStr);
// I have just made the model class for where fromt he below you get the complete object and then added to the list and returned.
List<UserModel> users = [];
users.add(userModel);
print('This is the name : ${users[0].name}');
//final x=users.length.toString();
//debugPrint("records:" + users.length.toString());
//debugPrint("kkk:" + absentees.length.toString());
return users;
}
class StudentList2 extends StatefulWidget {
//MyHomePage(String branch);
final regdNo;
final name;
const StudentList2({
Key key,
this.regdNo,
this.name,
}) : super(key: key);
//final String branch;
//const StudentList({Key key, this.branch}) : super(key: key);
//MyHomePage(String branch);
// final String title;
// final String branch="";
// MyHomePage(String branch, {Key key, this.title}) : super(key: key);
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<StudentList2> {
bool visible = false;
//bool _btnEnabled = false;
//bool _validate = false;
// var _firstPress = true ;
//Color _iconColor = Colors.yellow;
//Color _iconColor2 = Colors.white;
//var poll;
//DateTime parsedDate;
#override
Widget build(BuildContext context) {
print(widget.regdNo.toString());
return Padding(
padding: const EdgeInsets.symmetric(vertical: 5.0),
child: new Card(
color: Theme.of(context).primaryColor,
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.fromLTRB(8.0, 8.0, 8.0, 2.0),
child: Container(
child: new Text(
widget.regdNo.toUpperCase(),
style: TextStyle(
color: Colors.yellowAccent,
fontWeight: FontWeight.bold,
fontSize: 15.0,
),
),
),
),
ListTile(
title: new Text(
widget.regdNo,
style: TextStyle(
color: Colors.black,
fontSize: 14.0,
),
),
subtitle: new Text(
(widget.name),
style: TextStyle(
color: Colors.black,
fontSize: 15.0,
),
),
),
//
],
)),
);
}
}
// This is not the good approach to create a model class just check the sample model class that i have created.
class User {
//final int index;
final String username;
final String name;
//final Float cgpa;
User(
this.username,
this.name,
);
}
And below is the sample Gif file for you :
As stated by #pskink the method _getcompanylist() is async. An async function runs asynchronously, which means that the rest of the program doesn't wait for it to complete. You can use a future builder to deal whit that or you can simply wait for it by using the await function. I believe that for your code snippet future builder is the better choice.
I have a screen, that draws multiple child widgets called EventCard. Each of this cards have a switch button that can be clicked and it will dispatch a redux action to fire a HTTP call in order to update the model in the database with one of the options ( switch button ).
After the http call is finished, we check the response from API and if everything is okay, we want to display the snackbar on the screen.
The problem occurs when we are Scaffold.of(buildContext).showSnackBar() function, as the buildContext is already dismounted, so i get the error:
Looking up a deactivated widget's ancestor is unsafe.
At this point the state of the widget's element tree is no longer stable.
To safely refer to a widget's ancestor in its dispose() method, save a reference to the ancestor by calling inheritFromWidgetOfExactType() in the widget's didChangeDependencies() method.)
Here is the build function of my screen component:
#override
Widget build(BuildContext context) {
return StoreConnector<AppState, EventScreenModel>(
model: EventScreenModel(),
builder: (BuildContext context, EventScreenModel model) {
final int childCount = model.events[model.currentRoleId] != null
? model.events[model.currentRoleId].length
: 0;
return Scaffold(
appBar: CustomAppBar(),
body: model.isLoading
? Center(
child: PulsingLogo(),
)
: Scrollbar(
child: CustomScrollView(
slivers: <Widget>[
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.only(
top: 24.0, left: 24.0, bottom: 10.0),
child: Text(
'Moje udalosti',
style: Theme.of(context).textTheme.headline,
),
),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
Event.Event event =
model.events[model.currentRoleId][index];
String role;
model.roles.forEach((i, acc) {
acc.forEach((listOfRoles) {
if (listOfRoles.academy_id == event.academy_id) {
role = listOfRoles.role;
}
});
});
return Padding(
padding:
const EdgeInsets.symmetric(horizontal: 16.0),
child: EventCard(
event: event,
currentRoleId: model.currentRoleId,
role: role,
),
);
}, childCount: childCount),
),
if (childCount == 0)
SliverToBoxAdapter(
child: Center(
child: Padding(
padding: const EdgeInsets.all(36.0),
child: Text('Nenašli sa žiadne udalosti.'),
),
),
),
SliverToBoxAdapter(
child: FractionallySizedBox(
widthFactor: 0.8,
child: RaisedButton(
onPressed: () {},
child: Text('Načítať dalšie'),
),
),
)
],
),
),
drawer: Menu(),
);
}
);
}
And here is the child EventCard component:
import 'package:academy_app/components/switch_button.dart';
import 'package:academy_app/main.dart';
import 'package:academy_app/screens/events_screen.dart';
import 'package:academy_app/state/event_state.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:academy_app/models/event.dart';
class EventCard extends StatelessWidget {
const EventCard(
{Key key,
#required this.event,
this.showAttendanceButton = true,
this.role,
this.currentRoleId})
: super(key: key);
final Event event;
final bool showAttendanceButton;
final String role;
final int currentRoleId;
bool get isGoing {
if (role != null && role != 'player')
return event.coaches != null
? event.coaches
.firstWhere((coach) => coach.id == currentRoleId)
.pivot
.participate
: false;
return event.players != null
? event.players
.firstWhere((player) => player.id == currentRoleId)
.pivot
.participate
: false;
}
_changeAttendance(context) {
store.dispatch(
SetEventAttendanceAction(
eventId: event.id,
isGoing: !isGoing,
buildContext: context,
roleId: currentRoleId),
);
}
String getDay(String date) => DateTime.parse(date).day.toString();
String getMonth(String date) =>
DateFormat('MMM').format(DateTime.parse(date)).toUpperCase();
String getTime(String date) =>
DateFormat('HH:mm').format(DateTime.parse(date));
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
if (event != null)
Card(
child: ListTile(
title: Text(
event.name,
style: Theme.of(context).textTheme.title,
),
subtitle: Text(
getTime(event.start_time) + '-' + getTime(event.end_time),
),
leading: Container(
margin: EdgeInsets.all(4.0),
padding: EdgeInsets.all(5.0),
decoration: BoxDecoration(
color: Colors.orange[400],
borderRadius: BorderRadius.circular(4.0),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
getDay(event.start_time),
style: TextStyle(color: Colors.white),
),
Text(
getMonth(event.end_time),
style: TextStyle(color: Colors.white),
),
],
),
),
trailing: AttendanceSwitchButton(
onPressed: () => _changeAttendance(context),
isGoing: isGoing,
small: true,
),
),
),
],
);
}
}
And finally, the redux action that is called on click, that is also the one where the error is thrown:
import 'dart:convert';
import 'package:academy_app/constants.dart';
import 'package:academy_app/models/event.dart';
import 'package:academy_app/models/role.dart';
import 'package:academy_app/models/serializers.dart';
import 'package:academy_app/state/roles_state.dart';
import 'package:academy_app/state/ui_state.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'app_state.dart';
class GetEventsAction extends BarrierAction {
final int roleId;
GetEventsAction({
#required this.roleId,
});
#override
Future<AppState> reduce() async {
try {
final Role role = RolesState.rolesMap(state)[roleId];
if (role == null) {
return state;
}
final response = await http.get(
UrlBuilder.uri('v1/events/${role.role}/${role.id}'),
headers: UrlBuilder.headers);
if (response.statusCode != 200) {
print(response.body);
return state;
}
List<Event> events =
List.from(json.decode(response.body)).map((dynamic value) {
return serializers.deserializeWith(Event.serializer, value);
}).toList();
events.sort((a, b) {
return DateTime.parse(b.updated_at)
.compareTo(DateTime.parse(a.updated_at));
});
return state.copy(events: {
...state.events,
role.id: events,
});
} catch (error) {
throw Exception('Failed to get trainings data from server: $error');
}
}
}
class SetEventAttendanceAction extends BarrierAction {
final int eventId;
final bool isGoing;
final BuildContext buildContext;
final int roleId;
SetEventAttendanceAction(
{#required this.eventId,
#required this.isGoing,
#required this.buildContext,
#required this.roleId});
#override
Future<AppState> reduce() async {
try {
final Role role = RolesState.rolesMap(state)[roleId];
final response = await http.post(
UrlBuilder.uri(isGoing
? 'v1/event/$eventId/${role.role}/$roleId/going'
: 'v1/event/$eventId/${role.role}/$roleId/not-going'),
headers: UrlBuilder.headers);
if (response.statusCode != 200) {
return state;
}
if (response.body == 'false') {
Scaffold.of(buildContext).showSnackBar(
SnackBar(
content: Text(
isGoing
? 'Nepodarilo sa prihlásiť na udalosť.'
: 'Nepodarilo sa odhlásiť z udalosti.',
//style: Theme.of(buildContext).textTheme.subtitle,
),
),
);
} else if (response.body == 'true') {
// store.dispatch(GetEventsAction(roleId: state.currentRoleId));
Scaffold.of(buildContext).showSnackBar(
SnackBar(
content: Text(
isGoing
? 'Boli ste prihlásený na udalosť.'
: 'Boli ste odhlásený z udalosti.',
//style: Theme.of(buildContext).textTheme.subtitle,
),
),
);
} else {
return state;
}
} catch (error) {
throw Exception('Failed to get training data from server: $error');
}
}
}
Any ideas?
You've probably found a solution by now but here goes:
My use case is pretty similar to yours. I managed to solve it by doing this:
(1) Create a global class which can be accessed by other widgets (globals.dart) -
import 'package:flutter/material.dart';
class Globals {
static BuildContext currentContext;
}
(2) Import globals.dart into each widget you would like to display the snackbar on
(3) In the widget's build function, let the global object know which is the currently active context:
Widget build(BuildContext context) {
Globals.currentContext = context;
... truncated
}
(4) In the widget you wish to display the snackbar on, use Globals.currentContext as the build context. In your case, it should be:
Scaffold.of(Globals.currentContext).showSnackBar
This is my first contribution to SO. Hope this is helpful to someone :)