How can I get variable data into a Class with ChangeNotifier in Flutter? - flutter

I have the following class which pulls in data from a remote JSON source, if I hardcode the URL into the class (daUrl) then it works just great.
But I am planning to use a variable URL which I want to feed into this class when I call it. But declaring the variable at the top just doesn't work.
Any ideas?
class ThreadData with ChangeNotifier
{
Map<String,dynamic> _map = {};
bool _error = false;
String _errorMessage = '';
int _isLoggedIn = 0;
int tid = 1836392;
Map<String,dynamic> get map => _map;
bool get error => _error;
String get errorMessage => _errorMessage;
int get isLoggedIn => _isLoggedIn;
//var daUrl = 'https://jsonplaceholder.typicode.com/todos/';
Future<void> get fetchData async {
final response = await post(Uri.parse(daUrl));
if ((response.statusCode == 200) && (response.body.length >0))
{
try
{
debugPrint('\Thread => Make Call => Success\n');
_map = json.decode(response.body);
_error = false;
}
catch(e)
{
_error = true;
_errorMessage = e.toString();
_map = {};
_isLoggedIn = 0;
}
}
else
{
_error = true;
_errorMessage = 'Error: It could be your internet connection';
_map = {};
_isLoggedIn = 0;
}
notifyListeners(); // if good or bad we will notify the listeners either way
}
void initialValues()
{
_map = {};
_error = false;
_errorMessage = '';
_isLoggedIn = 0;
notifyListeners();
}
}

You can pass it to its constructor:
class ThreadData with ChangeNotifier {
final String url;
// Defaults to some URL if not passed as parameter
ThreadData({this.url = 'https://jsonplaceholder.typicode.com/todos/'});
Map<String,dynamic> _map = {};
bool _error = false;
String _errorMessage = '';
int _isLoggedIn = 0;
int tid = 1836392;
Map<String,dynamic> get map => _map;
bool get error => _error;
String get errorMessage => _errorMessage;
int get isLoggedIn => _isLoggedIn;
Future<void> get fetchData async {
// Access the URL field here
final response = await post(Uri.parse(this.url));
if ((response.statusCode == 200) && (response.body.length >0))
{
try
{
debugPrint('\Thread => Make Call => Success\n');
_map = json.decode(response.body);
_error = false;
}
catch(e)
{
_error = true;
_errorMessage = e.toString();
_map = {};
_isLoggedIn = 0;
}
}
else
{
_error = true;
_errorMessage = 'Error: It could be your internet connection';
_map = {};
_isLoggedIn = 0;
}
notifyListeners(); // if good or bad we will notify the listeners either way
}
void initialValues()
{
_map = {};
_error = false;
_errorMessage = '';
_isLoggedIn = 0;
notifyListeners();
}
}
Then you can call it like that:
ThreadData(); // the URL will be https://jsonplaceholder.typicode.com/todos/
ThreadData(url: 'https://example.com'); // the URL will be https://example.com

Related

Unhandled Exception: Bad state: Tried to use PaginationNotifier after `dispose` was called

I have a StateNotifierProvider that calls an async function which loads some images from the internal storage and adds them to the AsyncValue data:
//Provider declaration
final paginationImagesProvider = StateNotifierProvider.autoDispose<PaginationNotifier, AsyncValue<List<Uint8List?>>>((ref) {
return PaginationNotifier(folderId: ref.watch(localStorageSelectedFolderProvider), itemsPerBatch: 100, ref: ref);
});
//Actual class with AsyncValue as State
class PaginationNotifier extends StateNotifier<AsyncValue<List<Uint8List?>>> {
final int itemsPerBatch;
final String folderId;
final Ref ref;
int _numberOfItemsInFolder = 0;
bool _alreadyFetching = false;
bool _hasMoreItems = true;
PaginationNotifier({required this.itemsPerBatch, required this.folderId, required this.ref}) : super(const AsyncValue.loading()) {
log("PaginationNotifier created with folderId: $folderId, itemsPerBatch: $itemsPerBatch");
init();
}
final List<Uint8List?> _items = [];
void init() {
if (_items.isEmpty) {
log("fetchingFirstBatch");
_fetchFirstBatch();
}
}
Future<List<Uint8List?>> _fetchNextItems() async {
List<AssetEntity> images = (await (await PhotoManager.getAssetPathList())
.firstWhere((element) => element.id == folderId)
.getAssetListRange(start: _items.length, end: _items.length + itemsPerBatch));
List<Uint8List?> newItems = [];
for (AssetEntity image in images) {
newItems.add(await image.thumbnailData);
}
return newItems;
}
void _updateData(List<Uint8List?> result) {
if (result.isEmpty) {
state = AsyncValue.data(_items);
} else {
state = AsyncValue.data(_items..addAll(result));
}
_hasMoreItems = _numberOfItemsInFolder > _items.length;
}
Future<void> _fetchFirstBatch() async {
try {
_numberOfItemsInFolder = await (await PhotoManager.getAssetPathList()).firstWhere((element) => element.id == folderId).assetCountAsync;
state = const AsyncValue.loading();
final List<Uint8List?> result = await _fetchNextItems();
_updateData(result);
} catch (e, stk) {
state = AsyncValue.error(e, stk);
}
}
Future<void> fetchNextBatch() async {
if (_alreadyFetching || !_hasMoreItems) return;
_alreadyFetching = true;
log("data updated");
state = AsyncValue.data(_items);
try {
final result = await _fetchNextItems();
_updateData(result);
} catch (e, stk) {
state = AsyncValue.error(e, stk);
log("error catched");
}
_alreadyFetching = false;
}
}
Then I use a scroll controller attached to a CustomScrollView in order to call fetchNextBatch() when the scroll position changes:
#override
void initState() {
if (!controller.hasListeners && !controller.hasClients) {
log("listener added");
controller.addListener(() {
double maxScroll = controller.position.maxScrollExtent;
double position = controller.position.pixels;
if ((position > maxScroll * 0.2 || position == 0) && ref.read(paginationImagesProvider.notifier).mounted) {
ref.read(paginationImagesProvider.notifier).fetchNextBatch();
}
});
}
super.initState();
}
The problem is that when the StateNotifierProvider is fetching more data in the async function fetchNextBatch() and I go back on the navigator (like navigator.pop()), Flutter gives me an error:
[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Bad state: Tried to use PaginationNotifier after dispose was called.
Consider checking mounted.
I think that the async function responsible of loading data completes after I've popped the page from the Stack (which triggers a Provider dispose).
I'm probably missing something and I still haven't found a fix for this error, any help is appreciated.

Dart the operator [] isn't defined for 'Future<Map<String,Object>> Function(String)'

I'm trying to set up Auth0 for my Flutter app from this demo, but I ran into this issue. I've only been using Flutter for a little while, and I fixed a few errors, but can't fix this one. I checked for other answers here, and tried the documentation too. The error message is;
The operator [] isn't defined for 'Future<Map<String,Object>> Function(String)'
This is the method with the error;
Future<void> loginAction() async {
setState(() {
isBusy = true;
errorMessage = '';
});
try {
final AuthorizationTokenResponse? result =
await appAuth.authorizeAndExchangeCode(
AuthorizationTokenRequest(
AUTH0_CLIENT_ID,
AUTH0_REDIRECT_URI,
issuer: 'https://$AUTH0_DOMAIN',
scopes: ['openid', 'profile', 'offline_access'],
promptValues: ['login']
),
);
Map<String, Object> parseIdToken(String idToken) {
final parts = idToken.split(r'.');
assert(parts.length == 3);
return jsonDecode(
utf8.decode(base64Url.decode(base64Url.normalize(parts[1]))));
}
Future<Map<String, Object>> getUserDetails(String accessToken) async {
final url = 'https://$AUTH0_DOMAIN/userinfo';
final response = await http.get(Uri.parse(
url,
));
if (response.statusCode == 200) {
return jsonDecode(response.body);
} else {
throw Exception('Failed to get user details');
}
}
await secureStorage.write(
key: 'refresh_token', value: result!.refreshToken);
setState(() {
isBusy = false;
isLoggedIn = true;
name = parseIdToken['name'];
picture = getUserDetails['picture'];
});
} catch (e, s) {
print('login error: $e - stack: $s');
setState(() {
isBusy = false;
isLoggedIn = false;
errorMessage = e.toString();
});
}
}
And the lines that are erroring are;
name = parseIdToken['name'];
picture = getUserDetails['picture'];
Can this be fixed?
getUserDetails is a function, and so it doesn't have a [] operator.
Instead, try: var userDetails = await getUserDetails("token..."); picture = userDetails["picture"];

Send string values from one smartphone device to another smartphone device via Bluetooth in flutter

I want to send multiple string values from one smartphone device to another device via Bluetooth in flutter. I have seen flutter_blue and flutter_bluetooth_searial package examples also but i cant find anything that sends multiple strings values via Bluetooth. can anyone please suggest how i can be able to achieve this task?
I used flutter_bluetooth_searial in one of my projects, here is the the whole class including: sending data (multiple strings), receiving data, connect, disconnect, auto-pairing, ...etc
Hope you find this helpful.
import 'dart:async';
import 'dart:typed_data';
import 'dart:convert' show utf8;
import 'package:flutter/foundation.dart';
import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart';
class BluetoothStore extends ChangeNotifier {
var _instance = FlutterBluetoothSerial.instance;
var _dataBuffer = '';
void Function(String) _onDataCallback = (String data) {};
StreamSubscription<Uint8List> _dataSubscription;
StreamSubscription<BluetoothDiscoveryResult> _scannedDevicesSubscription;
BluetoothDiscoveryResult _connectedDevice;
BluetoothConnection _connection;
var name = "...";
var address = "...";
var pinNum = "1234";
var isScanning = false;
var _isConnecting = false;
var _autoPair = false;
var scannedDevices = List<BluetoothDiscoveryResult>();
var state = BluetoothState.UNKNOWN;
BluetoothStore() {
initBluetooth();
}
bool get autoPair => _autoPair;
bool get isConnecting => _isConnecting;
bool get isConnected => _dataSubscription != null;
set autoPair(bool value) {
_autoPair = value;
if (value) {
_instance.setPairingRequestHandler((request) {
if (request.pairingVariant == PairingVariant.Pin)
return Future.value(pinNum);
return Future.value("");
});
} else {
_instance.setPairingRequestHandler(null);
}
notifyListeners();
}
void initBluetooth() async {
// Get bluetooth initial state
this.state = await _instance.state;
this.address = await _instance.address;
this.name = await _instance.name;
notifyListeners();
_instance.onStateChanged().listen((state) {
this.state = state;
_reset();
});
}
Future<bool> pairWith(BluetoothDevice device) async {
var pairedDevices = await _instance.getBondedDevices();
if (pairedDevices.length < 7) {
var result = await _instance.bondDeviceAtAddress(device.address);
if (result) {
var deviceIndex = scannedDevices.indexWhere((scannedDevice) {
return scannedDevice.device.address == device.address;
});
scannedDevices[deviceIndex] = BluetoothDiscoveryResult(
device: BluetoothDevice(
name: device.name ?? '',
address: device.address,
type: device.type,
bondState:
result ? BluetoothBondState.bonded : BluetoothBondState.none,
),
rssi: scannedDevices[deviceIndex].rssi,
);
notifyListeners();
}
return Future.value(result);
}
return Future.value(false);
}
// Notice the return value
Future<bool> unpairFrom(BluetoothDevice device) async {
var result = await _instance.removeDeviceBondWithAddress(device.address);
if (result) {
var deviceIndex = scannedDevices.indexWhere((scannedDevice) {
return scannedDevice.device.address == device.address;
});
scannedDevices[deviceIndex] = BluetoothDiscoveryResult(
device: BluetoothDevice(
name: device.name ?? '',
address: device.address,
type: device.type,
bondState:
result ? BluetoothBondState.none : BluetoothBondState.bonded,
),
rssi: scannedDevices[deviceIndex].rssi,
);
notifyListeners();
}
return Future.value(result);
}
Future<bool> enable() => _instance.requestEnable();
Future<bool> disable() => _instance.requestDisable();
Future<void> openSettings() => _instance.openSettings();
Future<bool> connectTo(BluetoothDevice device) async {
_isConnecting = true;
if (isConnected) await _connection.close();
notifyListeners();
try {
var connection = await BluetoothConnection.toAddress(device.address);
_isConnecting = false;
_connection = connection;
_dataSubscription = connection.input.listen(_onDataReceived);
_dataSubscription.onDone(_onDisconnect);
var deviceIndex = scannedDevices.indexWhere((scannedDevice) {
return scannedDevice.device.address == device.address;
});
_connectedDevice = scannedDevices[deviceIndex] = BluetoothDiscoveryResult(
device: BluetoothDevice(
name: device.name ?? '',
address: device.address,
bondState: device.bondState,
type: device.type,
isConnected: true,
),
rssi: scannedDevices[deviceIndex].rssi,
);
notifyListeners();
return Future.value(true);
} catch (_) {
_isConnecting = false;
_dataSubscription = null;
_connection = null;
notifyListeners();
return Future.value(false);
}
}
Future<List<BluetoothDevice>> pairedDevices() async {
var result = await _instance.getBondedDevices();
return Future.value(result);
}
void disConnect() async {
if (isConnected) {
this.unpairFrom(_connectedDevice.device);
_connection.dispose();
}
}
void startScanning() {
isScanning = true;
scannedDevices.clear();
notifyListeners();
if (isConnected) scannedDevices.add(_connectedDevice);
_scannedDevicesSubscription = _instance.startDiscovery().listen((device) {
scannedDevices.add(device);
});
_scannedDevicesSubscription.onDone(() async {
isScanning = false;
notifyListeners();
});
}
void _onDataReceived(Uint8List data) {
// Allocate buffer for parsed data
var backspacesCounter = 0;
data.forEach((byte) {
if (byte == 8 || byte == 127) backspacesCounter++;
});
var buffer = Uint8List(data.length - backspacesCounter);
var bufferIndex = buffer.length;
// Apply backspace control character
backspacesCounter = 0;
for (int i = data.length - 1; i >= 0; i--) {
if (data[i] == 8 || data[i] == 127) {
backspacesCounter++;
} else {
if (backspacesCounter > 0) {
backspacesCounter--;
} else {
buffer[--bufferIndex] = data[i];
}
}
}
// Create message if there is new line character
var dataString = String.fromCharCodes(buffer);
var index = buffer.indexOf(13);
if (~index != 0) {
// \r\n
var data = backspacesCounter > 0
? _dataBuffer.substring(0, _dataBuffer.length - backspacesCounter)
: _dataBuffer = _dataBuffer + dataString.substring(0, index);
_onDataCallback(data);
_dataBuffer = dataString.substring(index);
} else {
_dataBuffer = (backspacesCounter > 0
? _dataBuffer.substring(0, _dataBuffer.length - backspacesCounter)
: _dataBuffer + dataString);
}
}
void _onDisconnect() {
// reset
if (this.state == BluetoothState.STATE_ON) {
var deviceIndex = scannedDevices.indexWhere((scannedDevice) {
return scannedDevice.device.address == _connectedDevice.device.address;
});
scannedDevices[deviceIndex] = BluetoothDiscoveryResult(
device: BluetoothDevice(
name: _connectedDevice.device.name ?? '',
address: _connectedDevice.device.address,
type: _connectedDevice.device.type,
bondState: _connectedDevice.device.bondState,
),
rssi: _connectedDevice.rssi,
);
}
_reset();
}
void _reset() {
_dataBuffer = '';
_isConnecting = false;
_dataSubscription = null;
_scannedDevicesSubscription = null;
_connectedDevice = null;
_connection = null;
if (this.state != BluetoothState.STATE_ON) {
scannedDevices.clear();
}
notifyListeners();
}
void onDataReceived(void Function(String) callback) {
_onDataCallback = callback;
}
bool sendData(String data) {
try {
data = data.trim();
if (data.length > 0 && isConnected) {
_connection.output.add(utf8.encode(data + "\r\n"));
return true;
}
} catch (e) {
return false;
}
return false;
}
void dispose() {
_instance.setPairingRequestHandler(null);
if (isConnected) _dataSubscription.cancel();
_scannedDevicesSubscription?.cancel();
super.dispose();
}
}
You can consume this class using ChangeNotifierProvider
ChangeNotifierProvider<BluetoothStore>.value(value: BluetoothStore())

Flutter: value of type 'Future<List<UserVideo>>' can't be assigned to a variable of type 'List<UserVideo>'

I am trying to use one List (custom type) but getting error.
When i try to use the getData() function. Like below.
List<UserVideo> videoDataList = [];
videoDataList = UserVideo.getData();
This is initState method.
#override
void initState() {
videoDataList = await UserVideo.getData();
WidgetsBinding.instance.addObserver(this);
_videoListController.init(
_pageController,
videoDataList,
);
super.initState();
}
I am getting the error.
A value of type 'Future<List<UserVideo>>' can't be assigned to a variable of type 'List<UserVideo>'.
Try changing the type of the variable, or casting the right-hand type to 'List<UserVideo>'.
Here is the code for function.
class UserVideo {
final String url;
final String image;
final String desc;
UserVideo({
this.url: mockVideo,
this.image: mockImage,
this.desc,
});
Future <List<UserVideo>> getData() async {
List<UserVideo> list = [];
try {
var deviceid = '123';
var dtgUid = '100';
var nodata;
var bodyss = {
"uid": dtgUid,
"deviceid": deviceid,
};
var url = 'http://192.168.100.4:8080/videos/get-data.php';
// Starting Web API Call.
var response = await http
.post(url, body: json.encode(bodyss))
.timeout(Duration(seconds: 5), onTimeout: () {
return null;
});
if (response.statusCode == 200) {
final data = StreamingFromJson(response.body);
if (data.count == null) {
count = 0;
} else {
count = data.count;
}
if (data.content.length > 0 && data.content[0].name != 'Empty') {
for (var i in data.content) {
list.add(UserVideo(image: i.thumbnail, url: i.video, desc: i.title));
}
} else {
nodata = 'No Record Found';
}
print(list.length);
}
} catch (e) {
print("Exception Caught: $e");
}
return list;
}
Edit:
Just showing the hardcoded value which is working fine.
static List<UserVideo> fetchVideo() {
List<UserVideo> list = [];
list.add(UserVideo(image: '', url: mockVideo, desc: 'Test1'));
list.add(UserVideo(image: '', url: mV2, desc: 'MV_TEST_2'));
list.add(UserVideo(image: '', url: mV3, desc: 'MV_TEST_3'));
list.add(UserVideo(image: '', url: mV4, desc: 'MV_TEST_4'));
return list;
}
I can use it like this and no error.
videoDataList = UserVideo.fetchVideo();
Your method getData() returns a Future:
Future<List<UserVideo>> getData() async {
List<UserVideo> list = [];
try {
var deviceid = '123';
var dtgUid = '100';
var nodata;
var bodyss = {
"uid": dtgUid,
"deviceid": deviceid,
};
You have to use async/await to call the method getData():
List<UserVideo> videoDataList = [];
videoDataList = await UserVideo.getData();
or use then():
List<UserVideo> videoDataList = [];
UserVideo.getData().then((list){
videoDataList = list;
});
Note: To use await you need to declare a method async

issue with geting all data from sqflite database

i have been trying to get all my data from a sqflite database, when i try to get a single data, this works totally fine:
Future<dynamic> getUser() async {
final db = await database;
var res = await db.query("files");
if (res.length == 0) {
return null;
} else {
var resMap = res[0];
return resMap;
}
}
but when i try to get all data using a for loop like the example below, i get an error
Future<dynamic> getUser() async {
final db = await database;
var res = await db.query("files");
var resMap;
var count = res.length;
if (count != 0) {
for (int i = 0; i < count; i++) {
resMap.add(res[i]);
}
}
return resMap;
}
the error says:
The method 'forEach' was called on null.
Receiver: null
Tried calling: forEach(Closure: (dynamic, dynamic) => Null)
i understand that it says that I've got no data,
and i also tried to remove the if statement, but still no luck!
change this method:
EDIT
Future<List<Map>> getUser() async {
final db = await database;
var res = await db.query("files");
List<Map> resMap = [];
if (res != null res.length > 0) {
for (int i = 0; i < count; i++) {
resMap.add(res[i]);
}
return resMap;
} else
{
return null;
}
}
try this in you widget
List<Map> newUser = [];
#override
void initState() {
super.initState();
getUser();
}
getUser() async {
final _userData = await DBProvider.db.getUser();
if(_userData != null ){
setState(() {
newUser = _userData;
});
} else{
setState(() {
newUser =[];
});
}
}