I want to use Refresh indicator so that when you pull up the page you are in right now rebuilds i will share with you my code i have tried many times but really i can't find a straight way around it here is my code
class Companies {
final int id;
final String name;
final String companyLogo;
Companies({this.id, this.name, this.companyLogo});
factory Companies.fromJson(Map<String, dynamic> json) {
return Companies(
id: json['id'],
name: json['name'],
companyLogo: json['company_logo'],
);
}
}
Future<List<Companies>> fetchCompanies() async {
final response = await http.get('$webSiteUrl/company/api/fetch');
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
return parseCompanies(response.body);
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load the companies');
}
}
List<Companies> parseCompanies(String responseBody) {
final parsed = json.decode(responseBody).cast<Map<String, dynamic>>();
return parsed.map<Companies>((json) => Companies.fromJson(json)).toList();
}
class CompaniesPage extends StatefulWidget{
#override
_CompaniesState createState() => _CompaniesState();
}
class _CompaniesState extends State<CompaniesPage> {
var refreshKey = GlobalKey<RefreshIndicatorState>();
Future<List<Companies>> companies;
#override
void initState() {
super.initState();
companies = fetchCompanies();
}
Future<Null> refreshCompanies() async {
refreshKey.currentState?.show(atTop: false);
setState(() {
companies = fetchCompanies();
});
return await companies;
}
Widget build(BuildContext context) {
checkVersion(context);
return Scaffold(
body: Center(
child: FutureBuilder<List<Companies>>(
future: companies,
builder: (context, snapshot) {
if (snapshot.hasData) {
List<Companies> companies = snapshot.data;
if(companies.length >= 1){
return MainLayout(
RefreshIndicator(
key: refreshKey,
onRefresh: refreshCompanies,
child: GridView.count(
crossAxisCount: 2 ,
children: List.generate(companies.length, (index) {
return GestureDetector(
onTap: () => {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Categories(companies[index].id, companies[index].name)),
)},
child: CompaniesInterface(companies[index].id , companies[index].name , companies[index].companyLogo),
);
}),
),
),
);
}else{
return EmptyDataBase();
}
} else if (snapshot.hasError) {
return ConnectionError();
}
// By default, show a loading spinner.
return DefaultTabController(
length: 1,
child: TabBar(
indicatorColor: Colors.transparent,
tabs: <Widget>[
Tab(
child: LoadingBouncingGrid.square(
backgroundColor: Colors.cyan,
size: 40,
),
),
],
),
);
},
),
),
);
}
}
as you can see i have tested it but it isn't refreshing the page correctly what i want is how should i rebuild this page on pull up so the missing part from my code i think is refreshCompanies() function
Update :
class _CompaniesState extends State<CompaniesPage> {
StreamController<List<Companies>> companiesStreamController;
var refreshKey = GlobalKey<RefreshIndicatorState>();
Future<List<Companies>> fetchCompanies() async {
final response = await http.get('$webSiteUrl/company/api/fetch');
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
return parseCompanies(response.body);
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load the companies');
}
}
loadCompanies() async {
fetchCompanies().then((result) async {
companiesStreamController.add(result);
return result;
});
}
Future<Null> refreshCompanies() async {
refreshKey.currentState.show(atTop: true);
setState(() {
loadCompanies();
});
}
#override
void initState() {
checkVersion(context);
companiesStreamController = new StreamController();
Timer.periodic(Duration(seconds: 1), (_) => loadCompanies());
super.initState();
}
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: StreamBuilder<List<Companies>>(
stream: companiesStreamController.stream,
builder: (context, snapshot) {
if (snapshot.hasData) {
List<Companies> companies = snapshot.data;
if(companies.length >= 1){
return MainLayout(
RefreshIndicator(
onRefresh: refreshCompanies,
key: refreshKey,
child: GridView.count(
crossAxisCount: 2 ,
children: List.generate(companies.length, (index) {
return GestureDetector(
onTap: () => {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Categories(companies[index].id, companies[index].name)),
)},
child: CompaniesInterface(companies[index].id , companies[index].name , companies[index].companyLogo),
);
}),
),
),
);
}else{......rest of code
Add a StreamController:
StreamController<List<Companies>> dataController;
Initialize it in your initState:
dataController = StreamController();
Move fetchCompanies inside your widget and before returning the result add it to your stream:
var result = parseCompanies(response.body);
dataController.add(result);
Use a StreamBuilder instead of FutureBuilder:
StreamBuilder<List<Companies>>(
stream: dataController.stream,
builder: (context, snapshot) {
...
}
)
Related
I am having issue that my Blocbuilder is not getting updated even after i am emitting the changes.
Like when i am fetch the data from the firebase by using StreamBuilder, the data is being fetched correctly and i am storing it in the visibleItem list. I have added a search functionality in which i using bloc and whenever my onchanged method gets called i update the state of it and emit the updated result list, but the issue is the blocbuilder isn't getting that state. I have seen all this by adding debuggers in the code, the result is getting emitted correctly. Please take a look at the code.
CateGoryScreen in which i have fetched the data and showing the data
visibleItems =
searchState.results.where((item) => item.isVisible).toList();
so in this line i am seeing wether that item isVisble or not which is being updated in the searcBloc class
Widget build(BuildContext context) {
return StreamBuilder(
stream: categoryBloc.categories,
builder: (context, AsyncSnapshot<List<CategoryModel>> snapshot) {
if (snapshot.hasData) {
_searchBloc = SearchBloc(snapshot.data ?? []);
return _buildScreen();
} else {
return const Center(child: CircularProgressIndicator());
}
});
}
Widget _buildScreen() {
final double appBarHeight =
MediaQuery.of(context).padding.top + kToolbarHeight;
return WillPopScope(
onWillPop: () async {
return _showDiscardWidget(context);
},
child: MultiBlocProvider(
providers: [
BlocProvider<ItemSelectionCubit>(create: (_) => ItemSelectionCubit()),
BlocProvider<SearchBloc>(create: (_) => _searchBloc),
],
child: BlocBuilder<SearchBloc, SearchState>(
builder: (context, searchState) {
final bool shouldDisplayBackgorund =
_searchController.text.isEmpty &&
MediaQuery.of(context).viewInsets.bottom == 0;
visibleItems =
searchState.results.where((item) => item.isVisible).toList();
return BlocBuilder<ItemSelectionCubit, ItemSelectionState>(
builder: (context, itemSelectionState) {
return Scaffold(
backgroundColor: Colors.white,
body: SafeArea(
top: false,
child: CustomScrollView(
controller: _scrollController,
slivers: [
CustomSliverAppBar(
maxExtent: shouldDisplayBackgorund
? appBarBackgroundHeight
: appBarHeight,
minExtent: appBarHeight,
appBar: _popupAppbar(
appBarHeight, itemSelectionState, context),
background: shouldDisplayBackgorund
? _backgroundAppbar(itemSelectionState, context)
: null,
),
SliverList(
delegate: SliverChildListDelegate(
[
visibleItems.isNotEmpty
? CategoryItemWidget(itemSelectionState,
visibleItems, searchState.query)
: NoResultFoundWidget(
resultQuery: searchState.query ?? ""),
],
),
),
],
),
),
);
},
);
},
),
),
);
}
Widget _popupAppbar(double appBarHeight,
ItemSelectionState itemSelectionState, BuildContext context) {
return Container(
height: appBarHeight,
width: double.infinity,
decoration: const BoxDecoration(color: Colors.white, boxShadow: [
BoxShadow(color: Colors.black26, offset: Offset(1, 0), blurRadius: 5),
]),
child: Align(
alignment: Alignment.bottomCenter,
child: SearchAppBar(
hint: 'Search Category',
onChanged: (query) {
_searchBloc.add(SearchQueryChanged(query: query));
},
onPressedPrefix: () {
Navigator.of(context).pop();
},
onPressedSuffix: () {
BlocProvider.of<ItemSelectionCubit>(context).toggleView();
},
searchController: _searchController,
onPressedSuffixInSearchBar: () {
_searchController.clear();
_searchBloc.add(SearchCleared());
},
suffixIconPath: itemSelectionState.isGrid
? AppAssetsPath.gridIcon
: AppAssetsPath.listIcon,
isExpanding: visibleItems.isEmpty,
),
),
);
}
onChanged: (query) {
_searchBloc.add(SearchQueryChanged(query: query));
},
so in this i am changing my searchquery here
searcBloc class in which my all logic for search is
class SearchBloc extends Bloc<SearchEvent, SearchState> {
final List<CategoryModel> _items;
SearchBloc(this._items)
: super(SearchState(
query: null,
results: _items.map((item) => item..isVisible = true).toList())) {
on<SearchQueryChanged>((event, emit) {
emit(_mapSearchQueryChangedToState(event.query));
});
on<SearchCleared>((event, emit) {
emit(_mapSearchClearedToState());
});
}
void onSearchQueryChanged(String query) {
add(SearchQueryChanged(
query: query,
));
}
void onSearchCleared() {
add(SearchCleared());
}
SearchState _mapSearchQueryChangedToState(String query) {
final results = _items.map((item) {
item.isVisible =
item.displayName!.toLowerCase().contains(query.toLowerCase());
return item;
}).toList();
return SearchState(query: query, results: results);
}
SearchState _mapSearchClearedToState() {
final results = _items.map((item) {
item.isVisible = true;
return item;
}).toList();
return SearchState(query: null, results: results);
}
}
class SearchState {
final String? query;
final List<CategoryModel> results;
SearchState({this.query, required this.results});
}
abstract class SearchEvent {
const SearchEvent();
}
class SearchQueryChanged extends SearchEvent {
final String query;
const SearchQueryChanged({required this.query});
}
class SearchCleared extends SearchEvent {}
and categoryBloc where i am fetching the data is
class CategoryBloc {
final _categoryController = StreamController<List<CategoryModel>>.broadcast();
Stream<List<CategoryModel>> get categories => _categoryController.stream;
Future<void> fetchCategories() async {
try {
await getData<CategoryModel>(
path: 'common', builder: (data) => CategoryModel.fromJson(data))
.then((value) {
_categoryController.sink.add(value);
});
} catch (error) {
print(error);
_categoryController.sink.addError(error);
}
}
void dispose() {
_categoryController.close();
}
}
Future<List<T>> getData<T>(
{required String path,
required T Function(Map<String, dynamic>) builder}) async {
List<T> list = [];
try {
final response = await FirebaseFirestore.instance
.collection(path)
.doc('onboardingCategories')
.get();
final data = response.data();
final List<dynamic> rawData = data!['categories'];
list = rawData.map((item) => builder(item)).toList();
return list;
} on FirebaseException catch (e) {
if (kDebugMode) {
print("Failed with error '${e.code}': ${e.message}");
}
return list;
} catch (e) {
throw Exception(e.toString());
}
}
Before fetching the data i tried using sample data only , like creating the sample class and that time i was able to fetch the data correctly, idk what is happening now i am fetching the data correctly, all the data is getting displayed but when i am using search bar it's not updating. Any help would be appreciated
I want to access the variable totalPresences that I have in my API request where I sum up the values from a map. Then I want to display the variable in my widget inside a dialog. How can I do that? Thanks in advance!
Here is my code
Future<List<Presence>> getPresencesByAthleteId() async {
try {
final response = await http.get(
Uri.parse();
if (response.statusCode == 200) {
Map map = json.decode(response.body);
List<Presence>? presencesList = [];
map.forEach((key, value) {
presencesList.add(Presence(
date: map.entries.first.key, count: map.entries.first.value));
var values = map.values;
var totalPresences = values.reduce((sum, element) => sum + element); //this I want to display it in a text
});
return presencesList.toList();
}
} catch (e) {
logger.e(e.toString());
}
return getPresencesByAthleteId(depId, teamId, id, context);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder<List<Athlete>>(
...
secondary: IconButton(
icon: const Icon(Icons.history_outlined,
color: Colors.black, size: 25),
onPressed: () {
if (_athlete[i].currentMonthPresences! > 0) {
showDialog(
context: context,
builder: (BuildContext context) {
return SimpleDialog(
children: [
Column(
FutureBuilder<List<Presence>>(
future: getPresencesByAthleteId(_athlete[i].department!.id, widget._team.teamKey!.teamId, _athlete[i].id, context),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
...
}),
);
} else if (snapshot.hasError) {
logger.e('${snapshot.error}');
}
}),
Container(
child:
Row(
children: [
const Text(''), // HERE I WANT TO DISPLAY totalPresences
)
],
),
),
It was easier than I thought I just needed a setState inside my api request like this:
int total=0;
Future<List<Presence>> getPresencesByAthleteId() async {
try {
final response = await http.get(
Uri.parse();
if (response.statusCode == 200) {
Map map = json.decode(response.body);
List<Presence>? presencesList = [];
map.forEach((key, value) {
presencesList.add(Presence(
date: map.entries.first.key, count: map.entries.first.value));
var values = map.values;
var totalPresences = values.reduce((sum, element) => sum + element);
setState(() {
totalPresences = total;
});
});
return presencesList.toList();
}
} catch (e) {
logger.e(e.toString());
}
return getPresencesByAthleteId(depId, teamId, id, context);
}
and then just display in dialog
.
.
const Text($total),
I'm new to flutter. I'm working on small projects which is like a scanning QR app. Here, I used hive to store scanned data in box but the problem is, it is storing duplicate values.
I need to remove that duplicate data how to do it?
code:
class PageState extends State<PassthroughQrScanPage> {
final ApiRepository repository = ApiRepository(
apiClient: ApiClient(
httpClient: http.Client(),
),
);
#override
void initState() {
super.initState();
openBox();
Prefs().reload();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: BlocProvider(
create: (context) => PassthroughqrscanBloc(repository),
child: BlocConsumer<PassthroughqrscanBloc, PassthroughqrscanState>(
listener: (context, state) {
if (state is PassthroughqrscanEmpty) {
return scan(context);
}
if (state is PassthroughqrscanError) {
Navigator.pop(context);
ShowErrorMessage(context, state.error.message.toString());
}
if (state is PassthroughqrscanLoaded) {
List<Batch> cleared = [];
state.entity.batches.forEach((element) {
cleared.add(element);
});
// final clearedData =
// cleared.map((item) => jsonEncode(item)).toList();
// final uniqueJsonList = clearedData.toSet().toList();
// List result =
// uniqueJsonList.map((item) => jsonDecode(item)).toList();
var seen = Set<int>();
List<Batch> clearedData = cleared
.where((cleared) => seen.add(cleared.batchNumber!))
.toList();
// clearedData = [
// ...{...clearedData}
// ];
clearedData.forEach((element) {
debugPrint(
"check the values for all the sdasd ${element.batchNumber}");
box.add(Batch(
batchNumber: element.batchNumber,
isUsed: element.isUsed,
transactionId: element.transactionId));
});
print("adding ssssss ${box.values.toList()}");
// String json = jsonEncode(state.entity);
// print("------>>>>>>>>>>>D>S>D>>$json");
// Prefs().setPassthroughData(json);
Navigator.pop(context);
WidgetsBinding.instance.addPostFrameCallback((_) {
showDialog(
context: context,
builder: (ctxDialog) => PassDialog(
compoundCode: widget.compoundCode.toString(),
lotNo: widget.lotNo.toString(),
schedule_id: widget.schedule_id.toString(),
screenClosed: _screenWasClosed,
scheduleRange: widget.scheduleRange,
batchQty: widget.batchQty,
));
});
}
// return Container();
Center(
child: CircularProgressIndicator(),
);
},
builder: (context, state) {
if (state is PassthroughqrscanEmpty) {
return scan(context);
} else
return scan(context);
},
),
),
);
}
scan(BuildContext mcontext) =>
MobileScanner(
controller: MobileScannerController(facing: CameraFacing.back),
allowDuplicates: false,
onDetect: (barcode, args) {
if (barcode.rawValue == null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text("Scan correct QR"),
duration: Duration(milliseconds: 800)));
});
} else {
String code = barcode.rawValue ?? "";
debugPrint('Barcode found! $code');
if (code.isNotEmpty) {
// if (!_screenOpened) {
// _screenOpened = true;
passthroughData = jsonDecode(code);
passthroughQrScan =
PassthroughQrScanData.fromJson(passthroughData);
BlocProvider.of<PassthroughqrscanBloc>(mcontext)
..add(VerifyPassthroughBatch(
passthroughQrScan?.operationName ?? "",
widget.schedule_id.toString(),
passthroughQrScan?.transactionId ?? "",
passthroughQrScan?.transactionRange ?? ""));
buildShowDialog(context);
}
}
});
Widget ShowErrorMessage(BuildContext context, String error) {
print("------------------------------/./././$error");
WidgetsBinding.instance.addPostFrameCallback((_) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text("Scan correct QR"),
duration: Duration(milliseconds: 800)));
});
return scan(context);
}
Future<void> openBox() async {
box = await Hive.openBox<Batch>("GetBatches");
// box = Boxes.getAllBatches();
await box.clear();
debugPrint("wwwwwwwwwwwwwwwwwkekekkkkkkkkk${box.values}");
// await box.deleteAll(box.keys);
}
}
List<Batch> batched = [];
var data;
class PassDialog extends StatefulWidget {
// const PassDialog({Key? key}) : super(key: key);
String? schedule_id;
String? compoundCode;
String? lotNo;
final Function() screenClosed;
final String? scheduleRange;
final int? batchQty;
PassDialog(
{required this.schedule_id,
required this.compoundCode,
required this.lotNo,
required this.screenClosed,
required this.scheduleRange,
required this.batchQty});
#override
State<PassDialog> createState() => _PassDialogState();
}
class _PassDialogState extends State<PassDialog> {
#override
void initState() {
batched = box.values.toSet().toList();
print("got values check for $batched");
super.initState();
}
#override
Widget build(BuildContext context) {
// List<Batch> batch = box.get("GetBatches");
//Batch batch = box?.get("GetBatches");
return SizedBox(
width: 150,
height: 100,
child: AlertDialog(
content: SingleChildScrollView(
child: Column(
children: [
// batched.forEach((element) {
for (final q in batched)
Text(
'${q.batchNumber.toString()}--${q.isUsed.toString()}--${q.transactionId.toString()}'),
// }),
Row(
children: [
ElevatedButton(
onPressed: () {
// print(values);
widget.screenClosed();
Navigator.of(
context,
rootNavigator: true,
).pop(
context,
);
},
child: Text("Continue")),
SizedBox(
width: 10,
),
ElevatedButton(
onPressed: () {
print(widget.scheduleRange);
WidgetsBinding.instance.addPostFrameCallback((_) {
Navigator.push(
context,
new MaterialPageRoute(
builder: (_) => GluePassthroughUploadPage(
id: widget.schedule_id.toString(),
compoundCode:
widget.compoundCode.toString(),
lotNo: widget.lotNo.toString(),
scheduleRange: widget.scheduleRange,
batchQty: widget.batchQty,
// getAllBatch: getBatch,
)));
});
},
child: Text("Show Add page")),
],
),
],
),
),
// Text("hohohoooooo${batched[0].batchNumber}${batched[0].isUsed}"),
),
);
}
}
Future buildShowDialog(BuildContext context) {
return showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return Center(
child: CircularProgressIndicator(),
);
});
}
How to remove duplicate list of objects? I'm using Batch as model class. I tried many methods to solve this issue. toSet(), like that.......
For your Batch Model, you can filter with a function like this:
List<Batch> removeDuplicates(List<Batch> items) {
List<Batch> uniqueItems = []; // uniqueList
var uniqueIDs = items
.map((e) => e.uniqueID)
.toSet(); //list if UniqueID to remove duplicates
uniqueIDs.forEach((e) {
uniqueItems.add(items.firstWhere((i) => i.uniqueID == e));
}); // populate uniqueItems with equivalent original Batch items
return uniqueItems;//send back the unique items list
}
Replace uniqueID with the parameter you want to use for filtering duplicates
If you want dealing with more complex objects, store seen ids to the Set and filter away those ones that are already in the set.
final list = ['a', 'a', 'b', 'c', 'c'];
final seen = <String>{};
final uniqueList = list.where((str) => seen.add(str)).toList();
print(uniqueList); // => ['a', 'b', 'c']
With the help of this, You can easily get Unique data from list and it will remove duplicate value
uniqueList = uniqueList.toSet().toList();
I have a method that fetches a PatientLog from SQLite.However, This PatientLog table mapped to an object with a class named PatientLog. Inside this PatientLog class, several other objects such as Speciality, AttendingPhysician, Course, etc. I need to map these PatienLog records to a local object. However, I have to use nested Futures. I need to retrieve the data from this nested Future. Think of Future of Future.
This is my fetch method
Future<List<Future<PatientLog>>> getForms() async {
Database db = await instance.getDatabase;
List<Map<String, dynamic>> forms =
await db.query(_tablePatientLog, orderBy: 'id DESC');
Institute? institute;
AttendingPhysician? attendingPhysician;
Speciality? speciality;
Course? course;
List<Future<PatientLog>> list = forms.map((myMap) async {
int? courseId = myMap['course_id'] as int?;
int? specialityId = myMap['speciality_id'] as int?;
int? attendingId = myMap['attending_id'] as int?;
int? instituteId = myMap['institute_id'] as int?;
if (courseId != null) {
await getCourse(courseId).then((value) => course=value);
}
if (attendingId != null) {
await getAttending(attendingId).then((value) => attendingPhysician=value);
}
if (specialityId != null) {
await getSpeciality(specialityId).then((value) => speciality=value);
}
if (instituteId != null) {
await getInstitute(instituteId).then((value) => institute=value);
}
return PatientLog.fromMap(
myMap, institute, course, attendingPhysician, speciality);
}).toList();
return list;
}
I need to display that information on a screen. I get an error type 'List<Future<PatientLog>>' is not a subtype of type 'Future<Object?>?'
class _DraftsState extends State<Drafts> {
final SQFLiteHelper _helper = SQFLiteHelper.instance;
#override
void initState() {
super.initState();
_refresh();
}
late List<Future<PatientLog>> fromDatabase;
Future<dynamic> _refresh() async {
await _helper.getForms().then((value) async{
setState(() {
fromDatabase = value;
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder(
future: _helper.getForms(),
builder: (BuildContext context,
AsyncSnapshot snapshot) {
if (snapshot.hasData && snapshot.data!.isEmpty) {
return Center(
child: Text(
"Henüz kaydedilmiş taslak bulunmamaktadır.",
textAlign: TextAlign.center,
style: TEXT_STYLE,
));
}
if (snapshot.hasError) {
return Center(
child: Text(
'Sanırım bir şeyler ters gitti.',
style: TEXT_STYLE,
));
}
if (snapshot.connectionState == ConnectionState.done) {
return RefreshIndicator(
backgroundColor: Colors.grey[700],
color: LIGHT_BUTTON_COLOR,
onRefresh: _refresh,
child: SizedBox(
height: MediaQuery.of(context).size.height,
child: ListView.builder(
shrinkWrap: true,
physics: const BouncingScrollPhysics(
parent: AlwaysScrollableScrollPhysics(),
),
itemCount: snapshot.data!.length,
itemBuilder: (BuildContext context, int index) {
return FutureBuilder(
future: snapshot.data,
builder: (context,innerSnap) {
return Text(innerSnap.toString());/*CustomListTile(
formData: innerSnap.data[index],
index: index,
routeTo: 1,
isDeletable: true,
);*/
}
);
},
),
),
);
}
return const Center(
child: Text("Nothing")//spinkit,
);
}),
);
}
}
I am trying to display some initial data that gets pulled from a server in my app, I get the data but I am not able to display it. Here is my code please help
Class where data has to be displayed
import 'package:deep_pocket/models/data_feed.dart';
import 'package:deep_pocket/models/mock_data.dart';
import 'package:deep_pocket/widgets/menu_buttons.dart';
import 'package:deep_pocket/widgets/post_widget.dart';
import 'package:deep_pocket/screens/user_input.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:provider/provider.dart';
class feedScreen extends StatefulWidget {
static const route = '/feed-screen';
#override
State<feedScreen> createState() => _feedScreenState();
}
class _feedScreenState extends State<feedScreen> {
int filter = 0;
var _intstate = true;
void updateFilter(tx, context) {
setState(() {
filter = tx;
});
Navigator.of(context).pop();
}
#override
void initState() {
// TODO: implement initState
super.initState();
}
#override
void didChangeDependencies() {
if (_intstate) {
Provider.of<mockData>(context).fetchandAddPost();
}
_intstate = false;
// TODO: implement didChangeDependencies
super.didChangeDependencies();
}
void filterSheet(ctx) {
showModalBottomSheet(
context: ctx,
builder: (ctx) => Container(
height: 300,
child: SingleChildScrollView(
child: Container(
height: 280,
child: ListView.builder(
itemCount: Tag.length,
itemBuilder: (ctx, i) => TextButton(
onPressed: () {
return updateFilter(i, context);
},
child: Text(Tag[i]),
)),
)),
));
}
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider<mockData>(
create: (context) => mockData(),
builder: (context, child) {
var posts = context.select((mockData m) => m.items);
print(posts.length);
if (filter != 0) {
posts = posts.where((i) => i.tag == filter).toList();
}
return Scaffold(
// drawer: Drawer(
// // Populate the Drawer in the next step.
// ),
appBar: AppBar(
title: const Text("Home"),
actions: [
TextButton(
onPressed: () => {filterSheet(context)},
child: const Text(
"Filters",
style: TextStyle(color: Colors.white),
))
],
),
body: SingleChildScrollView(
child: Column(
children: [
menuButtons(),
Container(
padding: const EdgeInsets.all(8),
child: ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: posts.length,
itemBuilder: (ctx, i) => postWidget(post: posts[i])),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
//Waiting for result
var newData =
await Navigator.pushNamed(context, userInput.route);
if (newData != null) {
context.read<mockData>().addPost(newData as dataFeed);
}
},
child: const Icon(Icons.add)),
);
});
}
}
Class where I have my Provider and fetch setup
import 'package:deep_pocket/models/data_feed.dart';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
class mockData with ChangeNotifier {
List<dataFeed> _data = [
// dataFeed(
// id: DateTime.now().toString(),
// imgsrc: "https://i.pravatar.cc/150?u=a042581f4e29026704d",
// name: "Priyam Srivastava",
// title: "How to change room ?",
// tag: 1,
// text:
// "I would like to know the process of changing my room cause I have not been able to study, and my roomate always plays music and drinks too much then shouts all night, please tell me how",
// ),
// dataFeed(
// id: DateTime.now().toString(),
// imgsrc: "https://i.pravatar.cc/150?u=a042581f4e29026704d",
// title: "Anyone intresed in playing BGMI?",
// name: "Part Agarwal",
// tag: 2,
// text:
// "So I have been looing for a squad for a long time and now i have finally decided that I am gonna buckle up and ask you all to join me",
// ),
// dataFeed(
// id: DateTime.now().toString(),
// imgsrc: "https://i.pravatar.cc/150?u=a042581f4e29026704d",
// title: "How to solve this question in O(n) complexity",
// name: "Preet Singh",
// tag: 3,
// text:
// "So I have been looing for a squad for a long time and now i have finally decided that I am gonna buckle up and ask you all to join me",
// ),
];
List<dataFeed> get items {
return [..._data];
}
Future<void> fetchandAddPost() async {
var url = link;
try {
print("getting your data");
final response = await http.get(url);
final extractedData = json.decode(response.body) as Map<String, dynamic>;
final List<dataFeed> loadedPosts = [];
extractedData.forEach((key, value) {
loadedPosts.add(dataFeed(
id: key,
imgsrc: value['imgsrc'],
name: value['name'],
title: value['title'],
text: value['text'],
date: value['date']));
});
print(loadedPosts.length);
_data = loadedPosts;
print(_data.length);
print("got your data");
notifyListeners();
} catch (e) {
print(e);
// TODO
}
}
Future<void> addPost(dataFeed newpost) async {
var url = link;
try {
final response = await http.post(
url as Uri,
body: json.encode({
'imgsrc': newpost.imgsrc,
'name': newpost.name,
'title': newpost.title,
'text': newpost.text,
'tag': newpost.tag,
'date': newpost.date,
}),
);
final newPost = dataFeed(
id: json.decode(response.body)['name'],
imgsrc: newpost.imgsrc,
name: newpost.name,
title: newpost.title,
text: newpost.text,
tag: newpost.tag,
date: newpost.date);
_data.insert(0, newPost);
notifyListeners();
} catch (e) {
print(e);
// TODO
}
}
}
I am getting data from server but it isn't being displayed, if I add new data it gets displayed.
This is the basic use age for using Provider, fetch data from network, updating ListView and pagination.
class ExampleWidget extends StatelessWidget {
const ExampleWidget({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider<ExampleChangeNotifier>(
create: (_) => ExampleChangeNotifier.instance(),
builder: (_, child) {
return Selector<ExampleChangeNotifier, NetworkStatus>(
selector: (_, model) => model.networkStatus,
builder: (_, nStatus, __) => nStatus == NetworkStatus.Loading
? const Center(
child: CircularProgressIndicator(),
)
: nStatus == NetworkStatus.Error
? const Center(
child: Text('Your error widget'),
)
: Selector<ExampleChangeNotifier, int>(
selector: (_, model) => model.listLength,
builder: (_, length, __) => length > 0
? ListView.builder(
itemCount: length + 1,
itemBuilder: (context, index) {
if (index < length) {
var listItem = _.read<ExampleChangeNotifier>().list[index];
return SizedBox(
height: 60,
child: Text('List item: ${listItem.whatever}'),
);
} else {
return Center(
child: ElevatedButton(
onPressed: () {
_.read<ExampleChangeNotifier>().loadMore();
},
child: Selector<ExampleChangeNotifier, bool>(
selector: (_, model) => model.loadMoreRequest,
builder: (_, value, __) => value ? const Text('loading...') : const Text('load more'),
),
),
);
}
},
)
: const Center(
child: Text('No data found'),
),
),
);
},
);
}
}
enum NetworkStatus { Loading, Done, Error }
class ExampleChangeNotifier extends ChangeNotifier {
NetworkStatus _networkStatus = NetworkStatus.Loading;
NetworkStatus get networkStatus => _networkStatus;
final List<dynamic> _list = <dynamic>[];
List<dynamic> get list => _list;
int _listLength = 0;
int get listLength => _listLength;
int _skip = 0; //Send this in your request parameters and use for pagination, e.g (for mysql query) => ... DESC LIMIT _skip, 10
bool _loadMoreRequest = false;
bool get loadMoreRequest => _loadMoreRequest;
ExampleChangeNotifier.instance() {
_getDataFromNetwork();
}
Future<void> _getDataFromNetwork() async {
try {
//Make your http request
// For example : await http.get('https:example.com?skip=$_skip');
_loadMoreRequest = false;
// ... Parse your data
List<dynamic> networkData = <dynamic>[];
_networkStatus = NetworkStatus.Done;
if (networkData.isNotEmpty) {
for (var item in networkData) {
_list.add(item);
_listLength++;
}
}
notifyListeners();
} catch (e) {
_loadMoreRequest = false;
_networkStatus = NetworkStatus.Error;
notifyListeners();
}
}
Future<void> loadMore() async {
_skip = _listLength;
_loadMoreRequest = true;
notifyListeners();
await _getDataFromNetwork();
}
}