This is my Query provider
In this provider My data is not updating UI not clearing variable of this function clearRaisedQueryDetailsLoad()
import 'dart:developer';
import 'package:flutter/material.dart';
class QueryProvider with ChangeNotifier {
//raised query details
bool _isRaisedQueryDetailsLoad = false;
bool get isRaisedQueryDetailsLoad => _isRaisedQueryDetailsLoad;
List _raisedQueryDetailsData = [];
List get raisedQueryDetailsData => _raisedQueryDetailsData;
var _raisedQueryDetailsSubject = '';
String get raisedQueryDetailsSubject => _raisedQueryDetailsSubject;
var _raisedQueryDetailsAttachmentUrl = '';
String get raisedQueryDetailsAttachmentUrl =>
_raisedQueryDetailsAttachmentUrl;
var _raisedQueryDetailsStatus;
dynamic get raisedQueryDetailsStatus => _raisedQueryDetailsStatus;
void addIsRaisedQueryDetailsLoad(bool isRaisedQueryDetailsLoad1) async {
_isRaisedQueryDetailsLoad = isRaisedQueryDetailsLoad1;
notifyListeners();
}
void addRaisedQueryDetailsData(dynamic raisedQueryDetailsData1) async {
for (int i = 0; i < raisedQueryDetailsData1["query_details"].length; i++) {
_raisedQueryDetailsData.add(raisedQueryDetailsData1["query_details"][i]);
}
_raisedQueryDetailsSubject = raisedQueryDetailsData1["query_subject"];
_raisedQueryDetailsAttachmentUrl = raisedQueryDetailsData1["attachment_url"];
_raisedQueryDetailsStatus = raisedQueryDetailsData1["query_status"];
print("_raisedQueryDetailsData $_raisedQueryDetailsData");
print("_raisedQueryDetailsAttachmentUrl $_raisedQueryDetailsAttachmentUrl");
print("_raisedQueryDetailsStatus $_raisedQueryDetailsStatus");
print("_raisedQueryDetailsData ${_raisedQueryDetailsData.runtimeType}");
print("_raisedQueryDetailsData ${_raisedQueryDetailsData.length}");
notifyListeners();
}
getRaisedQueryDetailsData() {
return _raisedQueryDetailsData;
}
getRaisedQueryDetailsDataStatus() {
return _raisedQueryDetailsStatus;
}
getRaisedQueryDetailsDataAttachment() {
return _raisedQueryDetailsAttachmentUrl;
}
getIsRaisedQueryDetailsLoad() {
print(_isRaisedQueryDetailsLoad);
return _isRaisedQueryDetailsLoad;
}
getIsRaisedQueryDetailsSubject() {
return _raisedQueryDetailsSubject;
}
void clearRaisedQueryDetailsLoad() {
print("clear");
_raisedQueryDetailsStatus = null;
_raisedQueryDetailsSubject = '';
_raisedQueryDetailsAttachmentUrl = '';
_raisedQueryDetailsData.clear();
notifyListeners();
}
}
queryProvider.clearRaisedQueryDetailsLoad(); // Here When I am calling this api fuction multiple time then my provider data is storing duplicate data(This function is bellow)
and second issue is when I am calling raisedQueryDetailsApiCalled() this function from a dialog box then My UI data is not changing
QueryProvider queryProvider = QueryProvider();
#override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
queryProvider = Provider.of<QueryProvider>(context, listen: false);
});
// TODO: implement initState
super.initState();
print(widget.queryData["id"]);
raisedQueryDetailsApiCalled();
}
raisedQueryDetailsApiCalled() async {
queryProvider.clearRaisedQueryDetailsLoad(); // Here When I am calling this api fuction multiple time then my provider data is storing duplicate data
if (mounted) {
setState(() {
queryProvider.addIsRaisedQueryDetailsLoad(true);
});
}
apiManager.queryRaisedDetailsApi(context, widget.queryData["id"]).then(
(val) {
if (val["code"] == 200) {
setState(() {
queryProvider.addRaisedQueryDetailsData(val["data"]);
});
}
if (mounted) {
setState(() {
queryProvider.addIsRaisedQueryDetailsLoad(false);
});
}
},
);
}
This is my UI code
queryProvider.getIsRaisedQueryDetailsLoad()
? SingleChildScrollView(
child: Column(
children: [
ProductListSkeleton(),
ProductListSkeleton(),
ProductListSkeleton(),
ProductListSkeleton(),
ProductListSkeleton(),
ProductListSkeleton()
],
),
)
: queryProvider1.getRaisedQueryDetailsData().length == 0
? Container(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [Text("Data not available")],
),
),
)
: Column(
children: [
_bodyChat(queryProvider1),
],
),
Try using the getter methods you have already instead of the general methods and see how that works. And maybe for the widgets code try to make use of consumers.
Consumer Class - provider library
Related
I want to update a list of type SongModel when I enter a value in the TextField. However, the list is not updating when onChanged is called.
List<SongModel> songs = item.data!;
List<SongModel> filterSongs = [];
//showing the songs
return Column(
mainAxisSize: MainAxisSize.max,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
keyboardType: TextInputType.text,
controller: searchController,
onChanged: (value) {
//pass value for the search
getSearch(filterSongs,songs);
},
decoration: InputDecoration(............
getSearch() :
getSearch(List<SongModel> filterSongs,List<SongModel> songs)
{
var text = searchController.text;
if (text.isEmpty) {
setState(() {
filterSongs = songs;
});
}
print(songs.where((SongModel item) => item.title.toLowerCase().contains(text.toLowerCase())).toList());
print(text);
setState(() {
// search = text;
filterSongs = songs.where((SongModel item) => item.title.toLowerCase().contains(text.toLowerCase())).toList();
});
print(filterSongs.length);
}
Here the list is not updating with set state method.
In your getSearch method, you are setting the value of the parameter passed to getSearch, not the value of the list outside of that method. You should move the filterSongs list declaration outside of your build method anyways so that it isn't redeclared every time the screen is rebuilt.
class MyScreenClassState extends State<MyScreenClass>{
//Create State method here
List<SongModel> filterSongs = [];
//Build method here
}
getSearch(List<SongModel> songs)
{
var text = searchController.text;
if (text.isEmpty) {
setState(() {
filterSongs = songs;
});
}
print(songs.where((SongModel item) => item.title.toLowerCase().contains(text.toLowerCase())).toList());
print(text);
setState(() {
// search = text;
filterSongs = songs.where((SongModel item) => item.title.toLowerCase().contains(text.toLowerCase())).toList();
});
print(filterSongs.length);
}
If onChanged method doesn't work for your implementation, you can try this structure I think I will work for you.
final TextEditingController _controller = TextEditingController();
#override
void initState() {
super.initState();
_controller.addListener(_onControllerChange);
}
#override
void dispose() {
super.dispose();
_controller.dispose();
}
void _onControllerChange() async {
.....
}
I am trying for the past two days to Get user data from binance Api with the Binance test net key using package https://pub.dev/packages/binance_spot/example
I tried every way possible I learned as I am a beginner I am not able to get the user data from the user account
I also changed the links that work for the testnet finance API version
here is the example code snippet
import 'dart:async';
import 'package:binance_spot/binance_spot.dart';
import 'package:flutter/material.dart' hide Interval;
class BinanceScreen extends StatefulWidget {
const BinanceScreen({Key? key}) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<BinanceScreen> {
BinanceSpot binanceSpot = BinanceSpot(
key: "3HKUUgtwu8WFWh4q31C5bh1veQqMEkEbF07hpMrq8xnwYsWDnj0ZWgYQkvC3gnE0",
secret: "KWwvWOi4nu8s0Qsi87iJ523cfp9Jcl8mFwt2hZHptMyahhGsxnmvdURIxVa9zA74",
);
double lastClosePrice = 0;
String tradablePairs = "";
String lastEventData = "";
WsAccountUpdate? balance;
late StreamSubscription<dynamic> klineStreamSub;
late StreamSubscription<dynamic> userdataStreamSub;
#override
void initState() {
startKlineStream();
startUserdataStream();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Binance API tester"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text("Current BTC price : $lastEventData"),
// Text("Last userdataStream event : ${balance}"),
TextButton(
onPressed: getTradablePairs,
child: const Text("GET PAIRS"),
),
Expanded(
flex: 1,
child: SelectableText(
tradablePairs,
maxLines: 200,
minLines: 1,
),
),
],
),
),
);
}
void startKlineStream() {
var stream = binanceSpot.klineStream(
symbol: "BTCUSDT",
interval: Interval.INTERVAL_5m,
);
klineStreamSub = stream.listen(handleNewKline);
}
void handleNewKline(WsKlineEvent event) {
setState(() {
lastClosePrice = event.kline.close;
});
}
void startUserdataStream() async {
var response = await binanceSpot.createListenKey();
if (response.isRight) {
var stream = binanceSpot.userDataStream(listenKey: response.right);
userdataStreamSub = stream.listen(handleUserdataEvent);
} else {
lastEventData = response.left;
}
}
void handleUserdataEvent(dynamic event) {
if (event is WsAccountUpdate) {
lastEventData =
"Account update event : ${event.balances.length} balances updated";
} else if (event is WsBalanceUpdate) {
lastEventData = "Balance update event : ${event.asset} balance updated";
} else if (event is WsExecutionReport) {
lastEventData =
"Execution report event : status is ${event.orderStatus.toStr()}";
} else if (event is WsListOrderStatus) {
lastEventData =
"ListOrder update event : status is ${event.listOrderStatus}";
} else {
lastEventData = "Unknown event type : ${event.toString()}";
}
}
void getTradablePairs() async {
var response = await binanceSpot.exchangeInfo();
if (response.isLeft) {
tradablePairs = response.left;
} else {
var listSymbol = response.right.symbols.map((e) => e.symbol).toList();
tradablePairs = "";
for (var s in listSymbol) {
tradablePairs += "$s ";
}
}
}
#override
void dispose() {
klineStreamSub.cancel();
userdataStreamSub.cancel();
super.dispose();
}
}
and here is the class code snippet which I want to acess
class WsOcoOrder {
String symbol;
int orderId;
String clientOrderId;
WsOcoOrder.fromMap(Map m)
: symbol = m['s'],
orderId = m['i'],
clientOrderId = m['c'];
}
What is the Possible way to access this class and its data in the above-given code snippet
Please refer me to the solution or a Link from where I can learn and implement by myself
I have a problem with Streams. I'm getting null form the stream. My objective is to combine 2 futures so I'm using first future when user has not searched any parameters and other one when user searched because I have 2 endpoints one for searched list and one that has no filters. I'm not really sure if that's standard industry practice for app dev. If its not please tell me so and how should I set up back end
Widget Body(BuildContext context, TabController taby) {
Future<List<littleOffers>> searchdata = null;
Stream<List<littleOffers>> _dataSwitch() async* {
if (searchdata == null)
yield* Stream.fromFuture(fetchOffers());
else
yield* Stream.fromFuture(searchdata);
}
void resetSearchData() {
searchdata = null;
}
void setSearchData(Future<List<littleOffers>> data) {
searchdata = data;
}
return Column(
children: <Widget>[
Search_Sort(context),
Expanded(
child: StreamBuilder(
stream: _dataSwitch(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.none || !snapshot.hasData) {
print('project snapshot data is: ${snapshot.data}');
print('project snapshot state is: ${snapshot.connectionState.toString()}');
return Container();
} else {
return TabBarView(controller: taby, children: [
getList(snapshot.data),
getList(snapshot.data),
getList(snapshot.data),
getList(snapshot.data),
]);
}
}),
),
],
);
}
That could work but I think you missed async and await on setSearchData
void setSearchData(Future<List<littleOffers>> newData) {
setState(()async{
searchdata = await newData;
});
}
and to remove data
void clearSearchData() {
setState((){
searchdata = null;
});
}
You can define only one List<littleOffers> (not future) ;
Widget Body(BuildContext context, TabController taby) {
List<littleOffers> searchdata = null;
Future<void> setSearchData(Future<List<littleOffers>> newData) async {
var _data = await newData;
setState((){
searchdata = _data;
});
}
return Column(
children: <Widget>[
Search_Sort(context),
Expanded(
child: searchData == null ? Center(child: CircularProcessIndicator()) : yourWidget;
),
],
);
}
Creating Stream from 2 futures so that one is alway actice and other one is on stand by when switch is needed.
Stream<List<littleOffers>> _dataOfferSwitch() async* {
if (searchOfferData == null)
yield* Stream.fromFuture(fetchOffers());
else
yield* Stream.fromFuture(searchOfferData);}
I have two List for DropDownList in my form, I use BloC pattern, I yield 4 State for one Event in demand_bloc.dart page.
#override
Stream<DemandAddState> mapEventToState(DemandAddEvent event) async* {
if (event is InitListLoadEvent) {
yield* _setInitListToState(event);
}
}
Stream<DemandAddState> _setInitListToState(InitListLoadEvent event) async* {
yield InitListLoading();
try {
List data = await demandAddRepository.getEmployeeList();
yield EmployeeListLoaded(data: data);
List data2 = await demandAddRepository.getDayOffList();
yield DayOffListLoaded(data2: data2);
} catch (_) {
yield InitListError();
}
}
my demand_state.dart is
class InitListLoading extends DemandAddState {}
class InitListError extends DemandAddState {}
class EmployeeListLoaded extends DemandAddState {
final List data;
const EmployeeListLoaded({#required this.data}) : assert(data != null);
}
class DayOffListLoaded extends DemandAddState {
final List data2;
const DayOffListLoaded({#required this.data2}) : assert(data2 != null);
}
after, and on my demand_page.dart,
I need to give data, data2,
but unfortunately, data is became Null.
body: BlocProvider<DemandAddBloc>(
create: (context) {
return DemandAddBloc(demandAddRepository: demandAddRepository)
..add(InitListLoadEvent());
},
child: BlocBuilder<DemandAddBloc, DemandAddState>(
builder: (context, demandAddState) {
if (demandAddState is InitListLoading) {
return LoadingIndicator();
}
if (demandAddState is DayOffListLoaded) {
data2 = demandAddState.data2;
}
if (demandAddState is EmployeeListLoaded) {
data = demandAddState.data;
}
if (demandAddState is InitListError){ ... }
return ...
ListView(
children: <Widget>[
Form(
key: _formKey,
child: Column(
children: [
_buildDropDownFormField(data),
_buildDropDownFormField(data2),
....
I lost data after the end process
That's because BLoC only has 1 state at a time. What you can do is either make a separate BLoC to handle both state or make a new state class.
class ListLoaded extends DemandAddState {
final List<Employee> data;
final List<DayOff> data2;
}
body: BlocProvider<DemandAddBloc>(
create: (context) {
return DemandAddBloc(demandAddRepository: demandAddRepository)
..add(InitListLoadEvent());
},
child: BlocBuilder<DemandAddBloc, DemandAddState>(
builder: (context, demandAddState) {
if (demandAddState is InitListLoading) {
return LoadingIndicator();
}
if (demandAddState is ListLoaded) {
data = demandAddState.data;
data2 = demandAddState.data2;
return ...
ListView(
children: <Widget>[
Form(
key: _formKey,
child: Column(
children: [
_buildDropDownFormField(data),
_buildDropDownFormField(data2),
....
}
if (demandAddState is InitListError){
return ..
}
I changed my _state.dart
class InitListLoading extends DemandAddState {}
class InitListError extends DemandAddState {}
class InitListLoaded extends DemandAddState {
final List data;
final List data2;
const InitListLoaded({#required this.data, #required this.data2}) : assert(data != null, data2 != null);
}
and my _page.dart
...
if (demandAddState is InitListLoaded) {
dataDayOffList = demandAddState.data2;
dataEmployeeList = demandAddState.data;
}
...
my use case is to create a list view of articles (each item have the same look, there could be huge amount of articles, e.g. > 10000). I tried with
- ListView with ListView.builder: it supposes only to render the item when the item is displayed
- ScrollController: to determine when to load the next items (pagination)
- then I use List to store the data fetched from restful API using http, by adding the data from http to the List instance
this approach is OK, but in case the user keeps on scrolling pages, the List instance will have more and more items, it can crash with stack Overflow error.
If I don't call List.addAll(), instead I assign the data fetched from api, like: list = data;
I have problem that when the user scroll up, he/she won't be able to see the previous items.
Is there a good approach to solve this? Thanks!
import 'package:flutter/material.dart';
import 'package:app/model.dart';
import 'package:app/components/item.dart';
abstract class PostListPage extends StatefulWidget {
final String head;
DealListPage(this.head);
}
abstract class PostListPageState<T extends PostListPage> extends State<PostListPage> {
final int MAX_PAGE = 2;
DealListPageState(String head) {
this.head = head;
}
final ScrollController scrollController = new ScrollController();
void doInitialize() {
page = 0;
try {
list.clear();
fetchNextPage();
}
catch(e) {
print("Error: " + e.toString());
}
}
#override
void initState() {
super.initState();
this.fetchNextPage();
scrollController.addListener(() {
double maxScroll = scrollController.position.maxScrollExtent;
double currentScroll = scrollController.position.pixels;
double delta = 200.0; // or something else..
if ( maxScroll - currentScroll <= delta) {
fetchNextPage();
}
});
}
#override
void dispose() {
scrollController.dispose();
super.dispose();
}
void mergeNewResult(List<PostListItem> result) {
list.addAll(result);
}
Future fetchNextPage() async {
if (!isLoading && mounted) {
page++;
setState(() {
isLoading = true;
});
final List<PostListItem> result = await doFetchData(page);
setState(() {
if (result != null && result.length > 0) {
mergeNewResult(result);
} else {
//TODO show notification
}
isLoading = false;
});
}
}
Future doFetchData(final int page);
String head;
List<PostListItem> list = new List();
var isLoading = false;
int page = 0;
int pageSize = 20;
final int scrollThreshold = 10;
Widget buildProgressIndicator() {
return new Padding(
padding: const EdgeInsets.all(8.0),
child: new Center(
child: new Opacity(
opacity: isLoading ? 1.0 : 0.0,
child: new CircularProgressIndicator(),
),
),
);
}
#override
Widget build(BuildContext context) {
ListView listView = ListView.builder(
padding: const EdgeInsets.all(16.0),
itemBuilder: (BuildContext context, int index) {
if (index == list.length) {
return buildProgressIndicator();
}
if (index > 0) {
return Column(
children: [Divider(), PostListItem(list[index])]
);
}
return PostListItem(list[index]);
},
controller: scrollController,
itemCount: list.length
);
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text(head),
actions: <Widget>[
IconButton(
icon: Icon(Icons.search),
onPressed: () {
},
),
// action button
IconButton(
icon: Icon(Icons.more_horiz),
onPressed: () {
},
),
]
),
body: new RefreshIndicator(
onRefresh: handleRefresh,
child: listView
),
);
}
Future<Null> handleRefresh() async {
doInitialize();
return null;
}
}
in my case, when the list length is 600, I start to get stack overflow error like:
I/flutter ( 8842): Another exception was thrown: Stack Overflow
I/flutter ( 8842): Another exception was thrown: Stack Overflow
screen:
enter image description here
somehow flutter doesn't show any more details of the error.
I wrote some sample code for a related question about paginated scrolling, which you could check out.
I didn't implement cache invalidation there, but it would easily be extendable using something like the following in the getPodcast method to remove all items that are more than 100 indexes away from the current location:
for (key in _cache.keys) {
if (abs(key - index) > 100) {
_cache.remove(key);
}
}
An even more sophisticated implementation could take into consideration the scroll velocity and past user behavior to lay out a probability curve (or a simpler Gaussian curve) to fetch content more intelligently.