Flutter Riverpod : return 2 stream with StreamProvider - flutter

I have music player application using package Asset Audio Player and Flutter Riverpod as State Management. I want listen 2 things stream :
Listen current duration of song
Listen current song played
final currentSongPosition = StreamProvider.autoDispose<Map<String, dynamic>>((ref) async* {
final AssetsAudioPlayer player = ref.watch(globalAudioPlayers).state;
ref.onDispose(() => player.dispose());
final Stream<double> _first = player.currentPosition.map((event) => event.inSeconds.toDouble());
final Stream<double> _second =
player.current.map((event) => event?.audio.duration.inSeconds.toDouble() ?? 0.0);
final maps = {};
maps['currentDuration'] = _first;
maps['maxDurationSong'] = _second;
return maps; << Error The return type 'Map<dynamic, dynamic>' isn't a 'Stream<Map<String, dynamic>>', as required by the closure's context.
});
How can i return 2 stream into 1 StreamProvider then i can simply using like this :
Consumer(
builder: (_, watch, __) {
final _currentSong = watch(currentSongPosition);
return _currentSong.when(
data: (value) {
final _currentDuration = value['currentDuration'] as double;
final _maxDuration = value['maxDuration'] as double;
return Text('Current : $_currentDuration, Max :$_maxDuration');
},
loading: () => Center(child: CircularProgressIndicator()),
error: (error, stackTrace) => Text('Error When Playing Song'),
);
},
),

I would start by creating a StreamProvider for each Stream:
final currentDuration = StreamProvider.autoDispose<double>((ref) {
final player = ref.watch(globalAudioPlayers).state;
return player.currentPosition.map((event) => event.inSeconds.toDouble());
});
final maxDuration = StreamProvider.autoDispose<double>((ref) {
final player = ref.watch(globalAudioPlayers).state;
return player.current.map((event) => event?.audio.duration.inSeconds.toDouble() ?? 0.0);
});
Next, create a FutureProvider to read the last value of each Stream.
final durationInfo = FutureProvider.autoDispose<Map<String, double>>((ref) async {
final current = await ref.watch(currentDuration.last);
final max = await ref.watch(maxDuration.last);
return {
'currentDuration': current,
'maxDurationSong': max,
};
});
Finally, create a StreamProvider that converts durationInfo into a Stream.
final currentSongPosition = StreamProvider.autoDispose<Map<String, double>>((ref) {
final info = ref.watch(durationInfo.future);
return info.asStream();
});

This answer is another approach what #AlexHartford do.
Current solution for my case is using package rxdart CombineLatestStream.list(), this code should be look then :
StreamProvider
final currentSongPosition = StreamProvider.autoDispose((ref) {
final AssetsAudioPlayer player = ref.watch(globalAudioPlayers).state;
final Stream<double> _first = player.currentPosition.map((event) => event.inSeconds.toDouble());
final Stream<double> _second =
player.current.map((event) => event?.audio.duration.inSeconds.toDouble() ?? 0.0);
final tempList = [_first, _second];
return CombineLatestStream.list(tempList);
});
How to use it :
Consumer(
builder: (_, watch, __) {
final players = watch(globalAudioPlayers).state;
final _currentSong = watch(currentSongPosition);
return _currentSong.when(
data: (value) {
final _currentDuration = value[0];
final _maxDuration = value[1];
return Slider.adaptive(
value: _currentDuration,
max: _maxDuration,
onChanged: (value) async {
final newDuration = Duration(seconds: value.toInt());
await players.seek(newDuration);
context.read(currentSongProvider).setDuration(newDuration);
},
);
},
loading: () => const Center(child: CircularProgressIndicator()),
error: (error, stackTrace) => Text(error.toString()),
);
},
),
But this approach have drawback, the code is hard to readable. Especially when use in Consumer, I have to know the order of return result.

Related

Flutter - populating syncfusion calendar with data from Firebase

I am using the syncfusion_flutter_calendar package. My objective is to populate the calendar with data coming from Firestore.
When I try the code below, I am getting an error that I understand, but I do not find where to fix it. Please, can you help? Thank you.
Error : Unhandled Exception: type 'List' is not a subtype of type 'List'
var myQueryResult;
List<Color> _colorCollection = <Color>[];
MeetingDataSource? events;
final databaseReference = FirebaseFirestore.instance;
class CalendarLastTest extends StatefulWidget {
const CalendarLastTest({Key? key}) : super(key: key);
#override
State<CalendarLastTest> createState() => _CalendarLastTestState();
}
class _CalendarLastTestState extends State<CalendarLastTest> {
#override
void initState() {
_initializeEventColor();
getDataFromFireStore().then((results) {
SchedulerBinding.instance.addPostFrameCallback((timeStamp) {
setState(() {});
});
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('TEST AGENDA'),
),
body: SfCalendar(
view: CalendarView.month,
initialDisplayDate: DateTime.now(),
dataSource: events,
monthViewSettings: const MonthViewSettings(
appointmentDisplayMode: MonthAppointmentDisplayMode.indicator,
showAgenda: true),
),
);
}
Future<void> getDataFromFireStore() async {
var snapShotsValue = await myQuery();
final Random random = Random();
List<Meeting> list = snapShotsValue.docs
.map((e) => Meeting(
title: e.data()['name'],
description: e.data()['notes'],
from: DateFormat('yyyy-MM-dd HH:mm').parse(e.data()['start_Date']),
to: DateFormat('yyyy-MM-dd HH:mm').parse(e.data()['due_Date']),
backgroundColor: _colorCollection[random.nextInt(9)],
isAllDay: false))
.toList();
setState(() {
events = MeetingDataSource(list);
print (events);
});
}
Future myQuery () async {
// final provider = Provider.of<MeetingProvider>(context, listen: false);
//final provider = Provider.of<MeetingProvider> (context);
final uid = FirebaseAuth.instance.currentUser!.uid;
final path = 'Users/$uid/allTasks';
final currentQuery = FirebaseFirestore.instance.collection(path);
myQueryResult = currentQuery.where('done', isEqualTo : 'No');
myQueryResult =
myQueryResult.where('start_Date', isNotEqualTo: '');
// myQueryResult = myQueryResult.where('due_Date'.length, isEqualTo : 16);
final snapshot = await myQueryResult.get();
return snapshot;
}
void _initializeEventColor() {
_colorCollection = <Color>[];
_colorCollection.add(const Color(0xFF0F8644));
_colorCollection.add(const Color(0xFF8B1FA9));
_colorCollection.add(const Color(0xFFD20100));
_colorCollection.add(const Color(0xFFFC571D));
_colorCollection.add(const Color(0xFF36B37B));
_colorCollection.add(const Color(0xFF01A1EF));
_colorCollection.add(const Color(0xFF3D4FB5));
_colorCollection.add(const Color(0xFFE47C73));
_colorCollection.add(const Color(0xFF636363));
_colorCollection.add(const Color(0xFF0A8043));
}
}
The issue is that the children's type is ListMeeting> the map method did not return that information, resulting in the type exception. You must specify the type of argument (Meeting) to the map method in order to fix this error. Please see the code snippets below.
Future<void> getDataFromFireStore() async
{
var snapShotsValue = await myQuery();
final Random random = Random();
List<Meeting> list = snapShotsValue.docs
.map<Meeting>((e) => Meeting(
eventName: e.data()['name'],
// description: e.data()['notes'],
from: DateFormat('yyyy-MM-dd HH:mm').parse(e.data()['start_Date']),
to: DateFormat('yyyy-MM-dd HH:mm').parse(e.data()['due_Date']),
background: _colorCollection[random.nextInt(9)],
isAllDay: false))
.toList();
setState(() {
events = MeetingDataSource(list);
});
}
Future<void> getDataFromFireStore() async {
// get appointments
var snapShotsValue = await fireStoreReference
.collection("ToDoList")
.where('CalendarType', isNotEqualTo: 'personal')
.get();
// map meetings
List<Meeting> list = snapShotsValue.docs
.map((e) => Meeting(
eventName: e.data()['Subject'],
from: convertTimeStamp(e.data()['StartTime']), //write your own ()
to: convertTimeStamp(e.data()['EndTime']),
background: colorConvert(e.data()['color']), //write your own ()
isAllDay: e.data()['isAllDay'],
recurrenceRule: e.data()['RRULE'],
recurrenceId: e.id,
resourceIds: List.from(e.data()['resourceIds']),
notes: e.data()['notes'],
address: e.data()['Address'].toString(),
geolocation: e.data()['Location'],
calendarType: e.data()['CalendarType'],
id: e.reference,
key: e.id))
.toList();
//get staff then add all to MeetingDataSource
var snapShotsValue2 = await fireStoreReference
.collection("Users")
.where('isStaff', isEqualTo: true)
.get();
List<CalendarResource> resources = snapShotsValue2.docs
.map((e) => CalendarResource(
displayName: e.data()['display_name'],
id: e.reference,
image: NetworkImage(valueOrDefault<String>(
e.data()['photo_url'],
'https',
)),
))
.toList();
setState(() {
events = MeetingDataSource(list, resources);
_employeeCollection = resources;
});
}

Future builder returns null although my list is not empty

I have this future builder which loads a list of movies in my provider class. Whenever I reload my screen, the movies do not get returned. Below is the future builder
FutureBuilder(
future: movieData.getTrendingMovies(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
} else if (snapshot.hasData) {
return Swiper(
itemBuilder: (BuildContext context, i) {
return ChangeNotifierProvider(
create: (context) => Movie(),
child: MovieContainer(
imageUrl: movieData.movies[i].imageUrl,
id: movieData.movies[i].id,
rate: movieData.movies[i].rate,
title: movieData.movies[i].title,
),
);
},
itemCount: movieData.movies.length,
viewportFraction: 0.25,
scale: 0.4,
);
} else {
return Text(snapshot.error.toString()); // it returns null on the screen
}
}),
Also in my homescreen where I display my movies, after the build method, I create a listener(moviesData) to listen to all changes in the movies provider.
final movieData = Provider.of<Movies>(context, listen: false);
Below is also the methos which fetches the movies from a restfulAPI using http get request
Future<void> getTrendingMovies() async {
List<String> movieTitles = [];
List<String> movieImageUrls = [];
List<String> movieDescriptions = [];
List<String> movieReleaseDates = [];
List<String> movieRates = [];
List<String> movieIds = [];
const _apiKey = '******************************';
const url =
'https://api.themoviedb.org/3/trending/all/week?api_key=$_apiKey';
try {
final response = await http.get(Uri.parse(url));
if (response.statusCode >= 400) {
print(response.statusCode);
return;
}
final extractedData = json.decode(response.body);
List moviesList = extractedData['results'] as List;
List<Movie> loadedMovies = [];
for (int i = 0; i < moviesList.length; i++) {
String movieTitle = moviesList[i]['original_title'] ?? '';
String? movieImage =
'https://image.tmdb.org/t/p/w400${moviesList[i]['poster_path']}'; //results[0].poster_path
String movieDescription =
moviesList[i]['overview'] ?? ''; //results[0].overview
String movieReleaseDate = moviesList[i]['release_date'] ?? '';
String? movieRate = moviesList[i]['vote_average'].toString();
String? movieId = moviesList[i]['id'].toString();
movieTitles.add(movieTitle);
movieImageUrls.add(movieImage);
movieDescriptions.add(movieDescription);
movieReleaseDates.add(movieReleaseDate);
movieRates.add(movieRate);
movieIds.add(movieId);
loadedMovies.add(
Movie(
id: movieIds[i],
title: movieTitles[i],
imageUrl: movieImageUrls[i],
description: movieDescriptions[i],
rate: double.parse(movieRates[i]),
releaseDate: movieReleaseDates[i],
),
);
}
_movies = loadedMovies;
notifyListeners();
//print(_movies.last.title); //This prints the name of the last movie perfectly....This gets called unlimited times whenever I set the listen of the **moviesData** to true
} catch (error) {
print(error);
}
}
There's a couple of things to unpack here.
Instead of a ChangeNotifierProvider, I believe you should use a Consumer widget that listens to your Movies provided service when you call the notifyListeners call, so make it Consumer<Movie>.
You can still call it using the Provider.of above for the sake of making the async call via the FutureBuilder, but I believe because you're not returning anything out of the getTrendingMovies and is just a Future<void> and you're querying the snapshot.hasData, well there is no data coming through the snapshot. Maybe instead you should call snapshot.connectionState == ConnectionState.done as opposed to querying for whether it has data.
Make sure that the response.body is truly returning a JSON value, but I believe your issue is in one of the points above.

Is there a way to access data coming from BLE device from single file which keeps updating?

I want to access characteristic values of BLE from one dart file, What I am doing is that I am connecting the device from one activity and then sending the device info to all other activities. But to get values I have to write the same code again and again to all activities/dart files.
For example i am connecting device in an activity like this:
StreamBuilder<List<ScanResult>>(
stream: FlutterBlue.instance.scanResults,
initialData: [],
builder: (c, snapshot) => Column(
children: snapshot.data
.map(
(r) => ScanResultTile(
result: r,
onTap: () => Navigator.of(context)
.push(MaterialPageRoute(builder: (context) {
r.device.connect();
print('DEVICE CONNECTED');
return BluetoothConnectedSuccess(device: r.device);
Here device: r.device is the device that i have connected to my Flutter App. Now if i want to display device data i have to initilaze these lines of code everytime i jump to any screen/activity:
class BluetoothConnectedSuccess extends StatefulWidget {
const BluetoothConnectedSuccess({Key key, this.device}) : super(key: key);
final BluetoothDevice device;
#override
_BluetoothConnectedSuccessState createState() =>
_BluetoothConnectedSuccessState();
}
class _BluetoothConnectedSuccessState extends State<BluetoothConnectedSuccess> {
// BLE
final String SERVICE_UUID = "4fafc201-1fb5-459e-8fcc-c5c9c331914b";
final String CHARACTERISTIC_UUID = "beb5483e-36e1-4688-b7f5-ea07361b26a8";
bool isReady;
Stream<List<int>> stream;
List<int> lastValue;
List<double> traceDust = List();
#override
void initState() {
super.initState();
isReady = false;
connectToDevice();
}
connectToDevice() async {
await widget.device.connect();
discoverServices();
}
discoverServices() async {
List<BluetoothService> services = await widget.device.discoverServices();
services.forEach((service) {
if (service.uuid.toString() == SERVICE_UUID) {
service.characteristics.forEach((characteristic) {
if (characteristic.uuid.toString() == CHARACTERISTIC_UUID) {
characteristic.setNotifyValue(!characteristic.isNotifying);
stream = characteristic.value;
print(stream);
lastValue = characteristic.lastValue;
print(lastValue);
setState(() {
isReady = true;
});
}
});
}
});
}
_dataParser(List<int> data) {
var value = Uint8List.fromList(data);
print("stream.value: $value"); // stream.value: [33]
var hr = ByteData.sublistView(value, 0, 1);
print("Heart rate: ${hr.getUint8(0)}");
return hr.getUint8(0); // Heart rate: 33
}
It's creating a lot of mess to write the same code again and again to the activities where BLE data is needed.
Is there a way to only call this connected device from a single file instead of initializing the same code in every activity?
This is the link to my repo for a look at what I am doing on every activity/screen with BLE device data.
Please help me out as I am new to Flutter. Thank you
Firstly learn basic of state management using Get you can refer to my code here what happens is every time something changes or upadtes it will immediate show in UI using specific Get Widgets like Obx and GetX , these widgets listen to changes in value which are marked with obs (observable).
for exmaple :
Obx(
() => ble.isScan.value ? LinearProgressIndicator() : SizedBox(),
),
this will observe the changes in isScan value .
class BleServices extends GetxController {
FlutterBlue blue = FlutterBlue.instance;
BluetoothDevice d;
var connectedDevices = List<BluetoothDevice>().obs;
var scanResults = List<ScanResult>().obs;
var bleState = BluetoothState.off.obs;
var isScan = false.obs;
var scanRequire = false.obs;
var bluetoothServices = List<BluetoothService>().obs;
var discoveringServices = false.obs;
var characteristics = List<BluetoothCharacteristic>().obs;
#override
void onInit() async {
super.onInit();
final perb = await Permission.bluetooth.status.isGranted;
final perL = await Permission.location.status.isGranted;
if (perb && perL) {
getConnectedDevices();
} else {
await Permission.bluetooth.request();
await Permission.location.request();
}
isScanning();
state();
}
isDiscovering() async {}
getConnectedDevices() async {
final connectedDevice = await blue.connectedDevices;
connectedDevices.value = connectedDevice;
AppLogger.print('connected devices : $connectedDevice');
if (connectedDevice.length == 0) {
scanRequire.value = true;
searchDevices();
}
return connectedDevice;
}
searchDevices() {
// AppLogger.print('pppppppppppppp');
blue
.scan(timeout: Duration(seconds: 20))
.distinct()
.asBroadcastStream()
.listen((event) {
AppLogger.print(event.toString());
scanResults.addIf(!scanResults.contains(event), event);
});
Future.delayed(Duration(seconds: 20), () {
blue.stopScan();
Get.showSnackbar(GetBar(
message: 'scan is finished',
));
});
}
isScanning() {
blue.isScanning.listen((event) {
AppLogger.print(event.toString());
isScan.value = event;
});
}
state() {
blue.state.listen((event) {
AppLogger.print(event.toString());
bleState.value = event;
});
}
}

type 'Future<Uint8List>' is not a subtype of type 'Widget'

I'm trying to display a pdf using this button in my flutter app but I keep getting the error in the title
AppButton.buildAppButton(
context,
AppButtonType.TEXT_OUTLINE,
'Generate PDF',
Dimens.BUTTON_BOTTOM_DIMENS, onPressed: () {
Sheets.showAppHeightEightSheet(
context: context,
widget: work(),
);
})
This is my work widget
Widget work() {
dynamic pdf = generateInvoice(PdfPageFormat.a4);
return pdf;
}
which calls this function. However I keep getting the title error type 'Future' is not a subtype of type 'Widget'. Any help is appreciated. end goal is to view the pdf but I'm not sure how ti get there. Thanks!
Future<Uint8List> generateInvoice(PdfPageFormat pageFormat) async {
final lorem = pw.LoremText();
final products = <Product>[
Product('19874', lorem.sentence(4), 3.99, 2),
Product('98452', lorem.sentence(6), 15, 2),
Product('28375', lorem.sentence(4), 6.95, 3),
Product('95673', lorem.sentence(3), 49.99, 4),
Product('23763', lorem.sentence(2), 560.03, 1),
Product('55209', lorem.sentence(5), 26, 1),
Product('09853', lorem.sentence(5), 26, 1),
];
final invoice = Invoice(
invoiceNumber: '982347',
products: products,
customerName: 'Abraham Swearegin',
customerAddress: '54 rue de Rivoli\n75001 Paris, France',
paymentInfo:
'4509 Wiseman Street\nKnoxville, Tennessee(TN), 37929\n865-372-0425',
tax: .15,
baseColor: PdfColors.teal,
accentColor: PdfColors.blueGrey900,
);
return await invoice.buildPdf(pageFormat);
}
class Invoice {
Invoice({
this.products,
this.customerName,
this.customerAddress,
this.invoiceNumber,
this.tax,
this.paymentInfo,
this.baseColor,
this.accentColor,
});
final List<Product> products;
final String customerName;
final String customerAddress;
final String invoiceNumber;
static const _darkColor = PdfColors.blueGrey800;
static const _lightColor = PdfColors.white;
PdfColor get _baseTextColor =>
baseColor.luminance < 0.5 ? _lightColor : _darkColor;
PdfColor get _accentTextColor =>
baseColor.luminance < 0.5 ? _lightColor : _darkColor;
double get _total =>
products.map<double>((p) => p.total).reduce((a, b) => a + b);
double get _grandTotal => _total * (1 + tax);
PdfImage _logo;
Future<Uint8List> buildPdf(PdfPageFormat pageFormat) async {
// Create a PDF document.
final doc = pw.Document();
final font1 = await rootBundle.load('assets/roboto1.ttf');
final font2 = await rootBundle.load('assets/roboto2.ttf');
final font3 = await rootBundle.load('assets/roboto3.ttf');
_logo = PdfImage.file(
doc.document,
bytes: (await rootBundle.load('assets/logo.png')).buffer.asUint8List(),
);
// Add page to the PDF
doc.addPage(
pw.MultiPage(
pageTheme: _buildTheme(
pageFormat,
font1 != null ? pw.Font.ttf(font1) : null,
font2 != null ? pw.Font.ttf(font2) : null,
font3 != null ? pw.Font.ttf(font3) : null,
),
header: _buildHeader,
footer: _buildFooter,
build: (context) => [
_contentHeader(context),
_contentTable(context),
pw.SizedBox(height: 20),
_contentFooter(context),
pw.SizedBox(height: 20),
_termsAndConditions(context),
],
),
);
// Return the PDF file content
return doc.save();
}
EDIT:: I'm trying to do this now but i get a red underline under the commented line
class MyWidget extends StatelessWidget {
#override
Widget build(context) {
return FutureBuilder<Uint8List>(
future: generateInvoice(PdfPageFormat.a4),
builder: (context, AsyncSnapshot<Uint8List> snapshot) {
if (snapshot.hasData) {
return snapshot.data; //get a red underline here
} else {
return CircularProgressIndicator();
}
}
);
}
}
Your work function should look like this. Here two extra package I have used. For showing pdf flutter_pdfview and to save the pdf temporary path_provider.
Future<Widget> work() async{
Uint8List pdf = await generateInvoice(PdfPageFormat.a4);
File file = await getPdf(pdf);
return PDFView(
filePath: file.path,
autoSpacing: false,
pageFling: false,
);
}
EDITED:
Future<File> getPdf(Uint8List pdf) async{
Directory output = await getTemporaryDirectory();
file = File(output.path+"/name_of_the_pdf.pdf");
await file.writeAsBytes(pdf);
return file;
}
Now update your build method as
#override
Widget build(context) {
return FutureBuilder<Widget>(
future: work(),
builder: (BuildContext context, AsyncSnapshot<Widget> snapshot) {
if (snapshot.hasData) {
return snapshot.data; //get a red underline here
} else {
return CircularProgressIndicator();
}
}
);
}

Flutter Riverpod : How to Implement FutureProvider?

I using Flutter Riverpod package to handling http request. I have simple Http get request to show all user from server, and i using manage it using FutureProvider from Flutter Riverpod package.
API
class UserGoogleApi {
Future<List<UserGoogleModel>> getAllUser() async {
final result = await reusableRequestServer.requestServer(() async {
final response =
await http.get('${appConfig.baseApiUrl}/${appConfig.userGoogleController}/getAllUser');
final Map<String, dynamic> responseJson = json.decode(response.body);
if (responseJson['status'] == 'ok') {
final List list = responseJson['data'];
final listUser = list.map((e) => UserGoogleModel.fromJson(e)).toList();
return listUser;
} else {
throw responseJson['message'];
}
});
return result;
}
}
User Provider
class UserProvider extends StateNotifier<UserGoogleModel> {
UserProvider([UserGoogleModel state]) : super(UserGoogleModel());
Future<UserGoogleModel> searchUserByIdOrEmail({
String idUser,
String emailuser,
String idOrEmail = 'email_user',
}) async {
final result = await _userGoogleApi.getUserByIdOrEmail(
idUser: idUser,
emailUser: emailuser,
idOrEmail: idOrEmail,
);
UserGoogleModel temp;
for (var item in result) {
temp = item;
}
state = UserGoogleModel(
idUser: temp.idUser,
createdDate: temp.createdDate,
emailUser: temp.emailUser,
imageUser: temp.emailUser,
nameUser: temp.nameUser,
tokenFcm: temp.tokenFcm,
listUser: state.listUser,
);
return temp;
}
Future<List<UserGoogleModel>> showAllUser() async {
final result = await _userGoogleApi.getAllUser();
state.listUser = result;
return result;
}
}
final userProvider = StateNotifierProvider((ref) => UserProvider());
final showAllUser = FutureProvider.autoDispose((ref) async {
final usrProvider = ref.read(userProvider);
final result = await usrProvider.showAllUser();
return result;
});
After that setup, i simply can call showAllUser like this :
Consumer((ctx, read) {
final provider = read(showAllUser);
return provider.when(
data: (value) {
return ListView.builder(
itemCount: value.length,
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
final result = value[index];
return Text(result.nameUser);
},
);
},
loading: () => const CircularProgressIndicator(),
error: (error, stackTrace) => Text('Error $error'),
);
}),
it's no problem if http request don't have required parameter, but i got problem if my http request required parameter. I don't know how to handle this.
Let's say , i have another http get to show specific user from id user or email user. Then API look like :
API
Future<List<UserGoogleModel>> getUserByIdOrEmail({
#required String idUser,
#required String emailUser,
#required String idOrEmail,
}) async {
final result = await reusableRequestServer.requestServer(() async {
final baseUrl =
'${appConfig.baseApiUrl}/${appConfig.userGoogleController}/getUserByIdOrEmail';
final chooseURL = idOrEmail == 'id_user'
? '$baseUrl?id_or_email=$idOrEmail&id_user=$idUser'
: '$baseUrl?id_or_email=$idOrEmail&email_user=$emailUser';
final response = await http.get(chooseURL);
final Map<String, dynamic> responseJson = json.decode(response.body);
if (responseJson['status'] == 'ok') {
final List list = responseJson['data'];
final listUser = list.map((e) => UserGoogleModel.fromJson(e)).toList();
return listUser;
} else {
throw responseJson['message'];
}
});
return result;
}
User Provider
final showSpecificUser = FutureProvider.autoDispose((ref) async {
final usrProvider = ref.read(userProvider);
final result = await usrProvider.searchUserByIdOrEmail(
idOrEmail: 'id_user',
idUser: usrProvider.state.idUser, // => warning on "state"
);
return result;
});
When i access idUser from userProvider using usrProvider.state.idUser , i got this warning.
The member 'state' can only be used within instance members of subclasses of 'package:state_notifier/state_notifier.dart'.
It's similiar problem with my question on this, but on that problem i already know to solved using read(userProvider.state) , but in FutureProvider i can't achieved same result using ref(userProvider).
I missed something ?
Warning: This is not a long-term solution
Assuming that your FutureProvider is being properly disposed after each use that should be a suitable workaround until the new changes to Riverpod are live. I did a quick test to see and it does work. Make sure you define a getter like this and don't override the default defined by StateNotifier.
class A extends StateNotifier<B> {
...
static final provider = StateNotifierProvider((ref) => A());
getState() => state;
...
}
final provider = FutureProvider.autoDispose((ref) async {
final a = ref.read(A.provider);
final t = a.getState();
print(t);
});
Not ideal but seems like a fine workaround. I believe the intention of state being inaccessible outside is to ensure state manipulations are handled by the StateNotifier itself, so using a getter in the meantime wouldn't be the end of the world.