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();
}
}
Related
I'm developing an app using movies database API. I want to read a list of the movies and then choose one and display the details to user.
MovieDetailsBloc code:
import 'package:bloc/bloc.dart';
import 'package:bloc_concurrency/bloc_concurrency.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter_recruitment_task/movie_details/models/movie_details.dart';
import 'package:flutter_recruitment_task/services/api_service.dart';
import 'package:stream_transform/stream_transform.dart';
part 'movie_details_event.dart';
part 'movie_details_state.dart';
const throttleDuration = Duration(milliseconds: 100);
EventTransformer<E> throttleDroppable<E>(Duration duration) {
return (events, mapper) {
return droppable<E>().call(events.throttle(duration), mapper);
};
}
class MovieDetailsBloc extends Bloc<MovieDetailsEvent, MovieDetailsState> {
MovieDetailsBloc({required this.apiService}) : super(MovieDetailsState()) {
on<MovieSelected>(
_onMovieSelected,
transformer: throttleDroppable(throttleDuration),
);
}
final ApiService apiService;
Future<void> _onMovieSelected(MovieSelected event, Emitter<MovieDetailsState> emit) async {
try{
final movie = await _fetchMovieDetails(event.query);
return emit(state.copyWith(
status: MovieDetailsStatus.success,
movie: movie,
));
} catch (_) {
emit(state.copyWith(status: MovieDetailsStatus.failure));
}
}
Future<MovieDetails> _fetchMovieDetails(String id) async {
final movie = apiService.fetchMovieDetails(id);
return movie;
}
String shouldIWatchIt() {
final movie = state.movie;
if(movie.revenue - movie.budget > 1000000 && DateTime.now().weekday == DateTime.sunday){
return 'Yes';
} else {
return 'No';
}
}
}
MovieListPage code:
import 'package:flutter/material.dart';
import 'package:flutter_recruitment_task/movie_details/bloc/movie_details_bloc.dart';
import 'package:flutter_recruitment_task/movies/bloc/movies_bloc.dart';
import 'package:flutter_recruitment_task/movies/models/movie.dart';
import 'package:flutter_recruitment_task/pages/movie_details/movie_details_page.dart';
import 'package:flutter_recruitment_task/pages/movie_list/movie_card.dart';
import 'package:flutter_recruitment_task/pages/movie_list/search_box.dart';
import 'package:flutter_recruitment_task/services/api_service.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class MovieListPage extends StatefulWidget {
#override
_MovieListPage createState() => _MovieListPage();
}
class _MovieListPage extends State<MovieListPage> {
final ApiService apiService = ApiService();
late final MoviesBloc moviesBloc;
late final MovieDetailsBloc movieDetailsBloc;
void _onSearchBoxSubmitted(String text) {
moviesBloc.add(MoviesFetched(query: text));
}
#override
void initState() {
moviesBloc = MoviesBloc(apiService: apiService);
movieDetailsBloc = MovieDetailsBloc(apiService: apiService);
super.initState();
}
#override
void dispose() {
movieDetailsBloc.close();
moviesBloc.close();
super.dispose();
}
#override
Widget build(BuildContext context) => BlocProvider(
create: (_) => movieDetailsBloc,
child: Scaffold(
appBar: AppBar(
actions: [
IconButton(
icon: Icon(Icons.movie_creation_outlined),
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => MovieDetailsPage(
movieDetailsBloc: movieDetailsBloc,
)
)
);
},
),
],
title: Text('Movie Browser'),
),
body: BlocProvider(
create: (_) => moviesBloc,
child: Column(
children: <Widget>[
SearchBox(onSubmitted: _onSearchBoxSubmitted),
Expanded(child: _buildContent()),
],
),
),
),
);
Widget _buildContent() => BlocBuilder<MoviesBloc, MoviesState>(
buildWhen: (previous, current) => previous != current,
builder: (context, state) {
switch (state.status) {
case MoviesStatus.failure:
return const Center(child: Text('failed to fetch movies'));
case MoviesStatus.success:
if (state.movies.isEmpty) {
return const Center(child: Text('no movies'));
}
return _buildMoviesList(state.movies);
case MoviesStatus.initial:
return const Center(child: CircularProgressIndicator());
}
});
Widget _buildMoviesList(List<Movie> movies) => ListView.separated(
separatorBuilder: (context, index) => Container(
height: 1.0,
color: Colors.grey.shade300,
),
itemBuilder: (context, index) => BlocBuilder<MovieDetailsBloc, MovieDetailsState>(
buildWhen: (previous, current) => previous != current,
builder: (context, state) {
return MovieCard(
title: movies[index].title,
rating: '${(movies[index].voteAverage * 10).toInt()}%',
color: state.movie.title == movies[index].title ?
Colors.amberAccent : Colors.white,
onTap: () {
movieDetailsBloc.add(MovieSelected(query: movies[index].id.toString()));
},
);
}
),
itemCount: movies.length,
);
}
MovieDetailsPage code:
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_recruitment_task/movie_details/bloc/movie_details_bloc.dart';
import 'package:flutter_recruitment_task/movie_details/models/movie_detail_placeholder.dart';
class MovieDetailsPage extends StatefulWidget {
final MovieDetailsBloc? movieDetailsBloc;
const MovieDetailsPage({super.key, this.movieDetailsBloc});
#override
_MovieDetailsPageState createState() => _MovieDetailsPageState();
}
class _MovieDetailsPageState extends State<MovieDetailsPage> {
var _details = [];
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) => BlocProvider(
create: (_) => widget.movieDetailsBloc!,
child: BlocBuilder<MovieDetailsBloc, MovieDetailsState>(
buildWhen: (previous, current) => previous != current,
builder: (context, state) {
_details = [
MovieDetailPlaceholder(title: 'Budget', content: '\$ ${state.movie.budget}'),
MovieDetailPlaceholder(title: 'Revenue', content: '\$ ${state.movie.revenue}'),
MovieDetailPlaceholder(title: 'Should I watch it today?', content: widget.movieDetailsBloc!.shouldIWatchIt()),
];
return Scaffold(
appBar: AppBar(
title: Text(state.movie.title),
),
body: ListView.separated(
separatorBuilder: (context, index) => Container(
height: 1.0,
color: Colors.grey.shade300,
),
itemBuilder: (context, index) => Container(
padding: EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
_details[index].title,
style: Theme.of(context).textTheme.headline5,
),
SizedBox(height: 8.0),
Text(
_details[index].content,
style: Theme.of(context).textTheme.subtitle1,
),
],
),
),
itemCount: _details.length,
),
);
}
),
);
}
I'm passing an instance of BLoC from MovieListPage to MovieDetailsPage because it holds the details of the movie to display. It works fine for one time, however when I go back to the MovieListPage I'm not able to choose a new movie to display the details. How I can make an instance of BLoC persist through navigation?
Create your bloc's intance after your bloc ends like
class MovieDetailsBloc extends Bloc<MovieDetailsEvent, MovieDetailsState> {
MovieDetailsBloc({required this.apiService}) : super(MovieDetailsState()) {
on<MovieSelected>(
_onMovieSelected,
transformer: throttleDroppable(throttleDuration),
);
}
final ApiService apiService;
Future<void> _onMovieSelected(MovieSelected event, Emitter<MovieDetailsState> emit) async {
try{
final movie = await _fetchMovieDetails(event.query);
return emit(state.copyWith(
status: MovieDetailsStatus.success,
movie: movie,
));
} catch (_) {
emit(state.copyWith(status: MovieDetailsStatus.failure));
}
}
Future<MovieDetails> _fetchMovieDetails(String id) async {
final movie = apiService.fetchMovieDetails(id);
return movie;
}
String shouldIWatchIt() {
final movie = state.movie;
if(movie.revenue - movie.budget > 1000000 && DateTime.now().weekday == DateTime.sunday){
return 'Yes';
} else {
return 'No';
}
}
}
MovieDetailsBloc movieDetailsBloc = MovieDetailsBloc();
and you can access it anywhere in your code with movieDetailsBloc.add or whatever you want.
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 am trying to add a search function in my flutter app, the search bar is showing and there's not errors but its not working and it doesn't return any results.
the data list is from an API that I already called using the rest API
// ignore_for_file: use_key_in_widget_constructors, avoid_print, avoid_unnecessary_containers, curly_braces_in_flow_control_structures, prefer_const_constructors, non_constant_identifier_names, unnecessary_new, avoid_function_literals_in_foreach_calls, unused_import, avoid_types_as_parameter_names, unused_label
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:myapp2/Service_Request/SR.dart';
import 'package:myapp2/main.dart';
import 'package:myapp2/Service_Request/second.dart';
import '../Classes/demandes.dart';
import 'SR_details.dart';
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: DataFromAPI(),
);
}
}
class DataFromAPI extends StatefulWidget {
#override
_DataFromAPIState createState() => _DataFromAPIState();
}
List<Attributes> _MyAllData = [];
var _srAttributes = [];
class _DataFromAPIState extends State<DataFromAPI> {
#override
void initState() {
loadData().then((value) {
setState(() {
_srAttributes.addAll(value);
});
});
super.initState();
}
Future<List<Sr>> loadData() async {
try {
var response = await http.get(Uri.parse(
'http://192.168.1.30:9080/maxrest/rest/mbo/sr/?_lid=azizl&_lpwd=max12345m&_format=json'));
if (response.statusCode == 200) {
final jsonBody = json.decode(response.body);
Demandes data = Demandes.fromJson(jsonBody);
final srAttributes = data.srMboSet.sr;
return srAttributes;
}
} catch (e) {
throw Exception(e.toString());
}
throw Exception("");
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: new Scaffold(
appBar: AppBar(
title: Text('Liste des Demandes'),
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () => Navigator.push(
context, MaterialPageRoute(builder: (context) => SR()))),
),
body: FutureBuilder<List<Sr>?>(
future: loadData(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return CircularProgressIndicator();
} else {
return new ListView.builder(
itemCount: snapshot.data?.length,
itemBuilder: ((_, index) {
return index == 0
? _searchbar()
: new ListTile(
title: new Card(
margin: new EdgeInsets.symmetric(
vertical: 2.0, horizontal: 8.0),
elevation: 10,
child: new ListTile(
title: new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(padding: new EdgeInsets.all(2.0)),
new Text(
'Ticket ID : ${snapshot.data![index].attributes.ticketid.content}'),
new Text(
'status : ${snapshot.data![index].attributes.status.content}'),
new Text(
'description : ${snapshot.data![index].attributes.description?.content}'),
new Text(
'Reported by : ${snapshot.data![index].attributes.reportedby.content}'),
new Text(
'Reoprt date : ${snapshot.data![index].attributes.statusdate.content}'),
],
),
trailing: Icon(Icons.arrow_forward_ios_rounded),
),
),
onTap: () {
Navigator.of(context)
.push(
new MaterialPageRoute(
builder: (BuildContext context) =>
new SrDetailsScreen(
sr: snapshot.data![index]),
),
)
.then((data) {});
});
}),
);
}
},
),
),
);
}
_searchbar() {
return Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
decoration: InputDecoration(hintText: "Search ..."),
onChanged: (text) {
text = text.toLowerCase();
setState(() {
_srAttributes = _MyAllData.where((srAttributes) {
var idticket = srAttributes.description!.content.toLowerCase();
return idticket.contains(text);
}).toList();
});
},
),
);
}
}
FutureBuilder loads values of current future. You are assigning a function result to FutureBuilder so its value always changes dynamically.
Create variable to keep Future's value.
Future<List<Sr>>? dataToLoad;
Whenever you want to load data from server ( for example, on text changed ):
setState((){
dataToLoad = loadData();
});
And use it in FutureBuilder:
FutureBuilder<List<Sr>?>(
future: dataToLoad,
I have the following class that is working fine.
class CartRiverpod extends StateNotifier<List<CartItemModel>> {
CartRiverpod([List<CartItemModel> products]) : super(products ?? []);
void add(ProductModel addProduct) {
bool productExists = false;
for (final product in state) {
if (product.id == addProduct.id) {
print("not added");
productExists = true;
}
else {
}
}
if (productExists==false)
{
state = [
...state, new CartItemModel(product: addProduct),
];
print("added");
}
}
void remove(String id) {
state = state.where((product) => product.id != id).toList();
}
}
The code above works perfectly. In my shopping cart, I want to limit the order of products to just 1 unit, that is why I am doing the code above. It works as I expected.
The only thing now is that, I'd like to show a snackbar alerting the user that he or she can only order 1 unit of each product.
How do I add a snackbar inside my StateNotifier?
DON'T show snackbar here.
You need to listen to the needed value in the widget tree as the follows:
#override
Widget build(BuildContext context, WidgetRef ref) {
ref.listen(cartListPageStateNotifierProvider, (value) {
// show snackbar here...
});
...
}
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/all.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'cart_list_page.freezed.dart';
final cartListPageStateNotifierProvider =
StateNotifierProvider((ref) => CartListPageStateNotifier(ref.read));
final cartListProvider = StateProvider((ref) {
return <CartListItemModel>[];
}); // this holds the cart list, at initial, its empty
class CartListPage extends StatefulWidget {
#override
_CartListPageState createState() => _CartListPageState();
}
class _CartListPageState extends State<CartListPage> {
final _scaffoldKey = GlobalKey<ScaffoldState>();
List<CartListItemModel> productsToBeAddedToCart = [
CartListItemModel(id: 1, name: "Apple"),
CartListItemModel(id: 2, name: "Tomatoes"),
CartListItemModel(id: 3, name: "Oranges"),
CartListItemModel(id: 4, name: "Ginger"),
CartListItemModel(id: 5, name: "Garlic"),
CartListItemModel(id: 6, name: "Pine"),
];
#override
Widget build(BuildContext context) {
return ProviderListener<CartListState>(
provider: cartListPageStateNotifierProvider.state,
onChange: (context, state) {
return state.maybeWhen(
loading: () {
final snackBar = SnackBar(
duration: Duration(seconds: 2),
backgroundColor: Colors.yellow,
content: Text('updating cart....'),
);
return _scaffoldKey.currentState.showSnackBar(snackBar);
},
success: (info) {
final snackBar = SnackBar(
duration: Duration(seconds: 2),
backgroundColor: Colors.green,
content: Text('$info'),
);
_scaffoldKey.currentState.hideCurrentSnackBar();
return _scaffoldKey.currentState.showSnackBar(snackBar);
},
error: (errMsg) {
final snackBar = SnackBar(
duration: Duration(seconds: 2),
backgroundColor: Colors.red,
content: Text('$errMsg'),
);
_scaffoldKey.currentState.hideCurrentSnackBar();
return _scaffoldKey.currentState.showSnackBar(snackBar);
},
orElse: () => SizedBox.shrink(),
);
},
child: Scaffold(
key: _scaffoldKey,
body: Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: Column(
children: [
SizedBox(height: 40),
Expanded(
child: ListView.builder(
itemCount: productsToBeAddedToCart.length,
itemBuilder: (context, index) {
final item = productsToBeAddedToCart[index];
return ListTile(
title: Text("${item.name}"),
leading: CircleAvatar(child: Text("${item.id}")),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: Icon(Icons.add),
onPressed: () => context
.read(cartListPageStateNotifierProvider)
.addProduct(item),
),
SizedBox(width: 2),
IconButton(
icon: Icon(Icons.remove),
onPressed: () => context
.read(cartListPageStateNotifierProvider)
.removeProduct(item),
),
],
),
);
},
),
)
],
),
),
),
);
}
}
class CartListPageStateNotifier extends StateNotifier<CartListState> {
final Reader reader;
CartListPageStateNotifier(this.reader) : super(CartListState.initial());
addProduct(CartListItemModel product) async {
state = CartListState.loading();
await Future.delayed(Duration(seconds: 1));
var products = reader(cartListProvider).state;
if (!products.contains(product)) {
reader(cartListProvider).state.add(product);
return state =
CartListState.success("Added Successfully ${product.name}");
} else {
return state = CartListState.error(
"cannot add more than 1 product of the same kind");
}
}
removeProduct(CartListItemModel product) async {
state = CartListState.loading();
await Future.delayed(Duration(seconds: 1));
var products = reader(cartListProvider).state;
if (products.isNotEmpty) {
bool status = reader(cartListProvider).state.remove(product);
if (status) {
return state =
CartListState.success("removed Successfully ${product.name}");
} else {
return state;
}
}
return state = CartListState.error("cart is empty");
}
}
#freezed
abstract class CartListState with _$CartListState {
const factory CartListState.initial() = _CartListInitial;
const factory CartListState.loading() = _CartListLoading;
const factory CartListState.success([String message]) = _CartListSuccess;
const factory CartListState.error([String message]) = _CartListError;
}
#freezed
abstract class CartListItemModel with _$CartListItemModel {
factory CartListItemModel({final String name, final int id}) =
_CartListItemModel;
}
I'm refactoring my app to GetX state management for less boilerplate code.
I make the Controller and the API provider (code below).
But when I want to refresh the data (Manually too) it won't change.
home_page.dart
class HomeUI extends GetView<HomeController> {
...
GetX<HomeController>(
initState: (state) => Get.find<HomeController>().getAll(),
builder: (_) {
return _.goalList.length < 1 ||
_.goalList == null
? Center(
child: Column(
mainAxisAlignment:
MainAxisAlignment.center,
children: [
CircularProgressIndicator(),
Text('0 goals found, please wait',
style: Theme.of(context)
.textTheme
.headline6
.copyWith(
color: kTextColor))
],
))
: ListView.builder(
itemBuilder: (context, index) {
GoalModel goalModel =
GoalModel.fromMap(
_.goalList[index]);
return ListTile(
title: Text(goalModel.text),
subtitle:
Text(goalModel.updated_at),
);
});
}
home_controller.dart
class HomeUI extends GetView<HomeController> {
...
class HomeController extends GetxController {
final MyRepository repository = MyRepository();
final _goalsList = RxList();
get goalList => this._goalsList.value;
set goalList(value) => this._goalsList.value = value;
getAll() {
repository.getAll().then((data) {
this.goalList = data;
update();
});
}
delete(id) {
repository.delete(id).then((message) {
this.goalList;
return message;
});
}
add(goal) {
repository.add(goal).then((data) {
this.goalList = data;
});
}
edit(editedItem, text, achievementDate) {
repository.edit(editedItem, text, achievementDate).then((data) {
this.goalList = data;
});
}
}
goals_repository.dart
class MyRepository {
final MyApiClient apiClient = MyApiClient();
getAll() {
return apiClient.getAll();
}
delete(id) {
return apiClient.deleteGoal(id);
}
edit(editedItem, text, achievementDate) {
return apiClient.updateGoal(editedItem, text, achievementDate);
}
add(goal) {
return apiClient.postGoal(goal);
}
}
api.dart (getAll() method)
getAll() async {
try {
var _token = await _sharedPrefsHelper.getTokenValue();
var response = await httpClient.get(baseUrl, headers: {
'Authorization': 'Bearer $_token',
});
if (response.statusCode == 200) {
print('json decode response is: ${json.decode(response.body)}');
return json.decode(response.body);
} else
print('erro -get');
} catch (error) {
print(error);
}
}
I followed this article to make the implementation:
getx_pattern
After updating manually your list, do:
this._goalsList.refresh()
After that your UI will be updated
Just Wrap the ListView.builder list with Obx or Getx. For widgets that are not in the list, you can wrap them individually with obx or getx.
Example:
Obx(() => ListView.builder(
physics: const NeverScrollableScrollPhysics(),
itemCount: item.length,
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
return Card()...
},
),
),
Obs Getx variables are only observed within an Obx or Getx as stated above. You need to wrap them up. Just be careful not to use Obx / Getx when there are no variables observed inside, as it will generate an error.
This answer is for #mjablecnik's comment:
class Other extends StatelessWidget {
final Counter c = Get.find();
final _chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890';
final Random _rnd = Random();
/* ---------------------------------------------------------------------------- */
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Obx(() => ListView.builder(
scrollDirection: Axis.vertical,
padding: EdgeInsets.all(10),
itemCount: c.testList.length,
itemBuilder: (context, index) => Card(
color: Colors.amber[600],
child: Padding(
padding: const EdgeInsets.all(10),
child: Center(
child: Text('${c.testList[index]}'),
),
),
),
)),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () => c.addToList(getRandomString(15)),
),
);
}
/* ---------------------------------------------------------------------------- */
// source: https://stackoverflow.com/questions/61919395/how-to-generate-random-string-in-dart
String getRandomString(int length) => String.fromCharCodes(Iterable.generate(
length, (_) => _chars.codeUnitAt(_rnd.nextInt(_chars.length))
)
);
}
Update 1:
Another little change I did was for the controller:
class Counter extends GetxController {
var count = 0.obs;
var testList = <String>['test1', 'test2'].obs;
/* ---------------------------------------------------------------------------- */
void incremenent() => count++;
/* ---------------------------------------------------------------------------- */
void addToList(String item) {
print('adding: $item');
testList.add(item);
}
}