I'm trying to develop "BLE Control App" with using flutter_Blue.
I added a tab bar so I want to Maintain Bluetooth State "Connect".
so I'm trying to use Provider, To set connection state but I have an error like this.
**======== Exception caught by foundation library ====================================================
The following assertion was thrown while dispatching notifications for BluetoothProvider:
setState() or markNeedsBuild() called during build.
This _InheritedProviderScope<BluetoothProvider> widget cannot be marked as needing to build because the framework is already in the process of building widgets. A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase.
The widget on which setState() or markNeedsBuild() was called was: _InheritedProviderScope<BluetoothProvider>
value: Instance of 'BluetoothProvider'
listening to value
The widget which was currently being built when the offending call was made was: Consumer<BluetoothProvider>
dirty
dependencies: [_InheritedProviderScope<BluetoothProvider>]
When the exception was thrown, this was the stack:
#0 Element.markNeedsBuild.<anonymous closure> (package:flutter/src/widgets/framework.dart:4138:11)
#1 Element.markNeedsBuild (package:flutter/src/widgets/framework.dart:4153:6)
#2 _InheritedProviderScopeElement.markNeedsNotifyDependents (package:provider/src/inherited_provider.dart:531:5)
#3 ChangeNotifier.notifyListeners (package:flutter/src/foundation/change_notifier.dart:243:25)
#4 BluetoothProvider.startScan (package:flutter_joystick/provider/bluetooth_provider.dart:46:5)
...
The BluetoothProvider sending notification was: Instance of 'BluetoothProvider'**
this is my bluetooth provider code
class BluetoothProvider with ChangeNotifier{
final String SERVICE_UUID = "0000ffe0-0000-1000-8000-00805f9b34fb";
final String CHARACTERISTIC_UUID="0000ffe1-0000-1000-8000-00805f9b34fb";
final String TARGET_DEVICE_NAME="HMSoft";
FlutterBlue flutterBlue = FlutterBlue.instance;
StreamSubscription<ScanResult> scanSubScription;
BluetoothDevice targetDevice;
BluetoothCharacteristic targetCharacteristic;
BluetoothState bluetoothState;
String connectionText="";
String joystick="";
startScan(){
connectionText="Start Scanning";
scanSubScription = flutterBlue.scan().listen((scanResult){
if(scanResult.device.name==TARGET_DEVICE_NAME){
print("Device Found");
stopScan();
connectionText="Found Target Device";
targetDevice = scanResult.device;
}
}, onDone: () => stopScan());
notifyListeners();
}
stopScan(){
scanSubScription?.cancel();
scanSubScription=null;
notifyListeners();
}
connectToDevice() async{
if(targetDevice==null) return;
connectionText = "Device Connecting";
await targetDevice.connect();
print("Device Connected");
connectionText="Device Connected";
discoverServices();
notifyListeners();
}
disconnectFromDevice(){
if(targetDevice==null) return;
targetDevice.disconnect();
connectionText="Device Disconnected";
notifyListeners();
}
discoverServices() async{
if(targetDevice==null) return;
List<BluetoothService> services = await targetDevice.discoverServices();
services.forEach((service) {
if(service.uuid.toString() == SERVICE_UUID){
service.characteristics.forEach((characteristc) {
if (characteristc.uuid.toString() == CHARACTERISTIC_UUID) {
targetCharacteristic = characteristc;
writeData("Connect Complete!\r\n");
connectionText = "All Ready with ${targetDevice.name}";
}
});
}
}
);
notifyListeners();
}
writeData(String data) async{
if(targetCharacteristic==null) return;
List<int> bytes = utf8.encode(data);
await targetCharacteristic.write(bytes);
notifyListeners();
}
}
Funny, the Bluetooth connection is progressing, but the error written above keeps coming up through the console window.
The first page of the Tab Bar is the joystick page, and Bluetooth is connected due to an error, but the joystick is not working.
Here is Joystick code
class JoyPad extends StatefulWidget {
#override
_JoyPadState createState() => _JoyPadState();
}
class _JoyPadState extends State<JoyPad> {
BluetoothProvider _bluetoothProvider;
#override
Widget build(BuildContext context) {
_bluetoothProvider = Provider.of<BluetoothProvider>(context,listen:false);
return Consumer<BluetoothProvider>(
builder:(context,provider,child) {
_bluetoothProvider.startScan();
return Scaffold(
appBar: AppBar(
title: Text(_bluetoothProvider.connectionText),
backgroundColor: Colors.indigoAccent,
actions: <Widget>[
IconButton(
icon: Icon(Icons.bluetooth), iconSize: 30,
onPressed: () {
_bluetoothProvider.connectToDevice();
print(_bluetoothProvider.bluetoothState.toString());
},
),
IconButton(
icon: Icon(Icons.bluetooth_disabled), iconSize: 30,
onPressed: () {
_bluetoothProvider.disconnectFromDevice();
print(_bluetoothProvider.bluetoothState.toString());
}),
],
),
body: joystickWidget(),
);
});
}
}
Additionally, the provider does not "setState" so I try to display connection text according to the status change on the App Bar, but it is not possible.
I would also appreciate it if you could tell me how to solve it.
You are actually encountering this error because you try to rebuild the widget tree while it's being build.
Your call on _bluetoothProvider.startScan();in your Consumer's builder method will call the notifyListeners method which actually tries to rebuild the tree while it's being build, thus that exception will be thrown.
WHY?
The Consumer widget is actually listening to changes on your BluetoothProvider; so when you call the notifyListeners on the BluetothProvider class, the Consumer tries to rebuild itself, which is not authorized.
A solution would be to first build the tree, and then call the startScan method.
You could try this:
Provider Code
class BluetoothProvider with ChangeNotifier{
final String SERVICE_UUID = "0000ffe0-0000-1000-8000-00805f9b34fb";
final String CHARACTERISTIC_UUID="0000ffe1-0000-1000-8000-00805f9b34fb";
final String TARGET_DEVICE_NAME="HMSoft";
FlutterBlue flutterBlue = FlutterBlue.instance;
StreamSubscription<ScanResult> scanSubScription;
BluetoothDevice targetDevice;
BluetoothCharacteristic targetCharacteristic;
BluetoothState bluetoothState;
String connectionText="";
String joystick="";
startScan() {
connectionText="Start Scanning";
scanSubScription = flutterBlue.scan().listen((scanResult){
if(scanResult.device.name==TARGET_DEVICE_NAME){
print("Device Found");
stopScan();
connectionText="Found Target Device";
targetDevice = scanResult.device;
}
}, onDone: () => stopScan());
notifyListeners();
}
stopScan() {
scanSubScription?.cancel();
scanSubScription=null;
notifyListeners();
}
connectToDevice() async{
if(targetDevice==null) return;
connectionText = "Device Connecting";
await targetDevice.connect();
print("Device Connected");
connectionText="Device Connected";
discoverServices();
notifyListeners();
}
disconnectFromDevice(){
if(targetDevice==null) return;
targetDevice.disconnect();
connectionText="Device Disconnected";
notifyListeners();
}
discoverServices() async {
if(targetDevice==null) return;
List<BluetoothService> services = await targetDevice.discoverServices();
services.forEach((service) {
if(service.uuid.toString() == SERVICE_UUID){
service.characteristics.forEach((characteristc) {
if (characteristc.uuid.toString() == CHARACTERISTIC_UUID) {
targetCharacteristic = characteristc;
writeData("Connect Complete!\r\n");
connectionText = "All Ready with ${targetDevice.name}";
}
});
}
});
notifyListeners();
}
writeData(String data) async{
if(targetCharacteristic==null) return;
List<int> bytes = utf8.encode(data);
await targetCharacteristic.write(bytes);
notifyListeners();
}
}
Widget code
class JoyPad extends StatefulWidget {
#override
_JoyPadState createState() => _JoyPadState();
}
class _JoyPadState extends State<JoyPad> {
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
// The code in this block will be executed after the build method
context.read<BluetoothProvider>().startScan();
});
}
#override
Widget build(BuildContext context) {
return Consumer<BluetoothProvider>(
builder:(context,provider,child) {
return Scaffold(
appBar: AppBar(
title: Text(_bluetoothProvider.connectionText),
backgroundColor: Colors.indigoAccent,
actions: <Widget>[
IconButton(
icon: Icon(Icons.bluetooth), iconSize: 30,
onPressed: () {
_bluetoothProvider.connectToDevice();
print(_bluetoothProvider.bluetoothState.toString());
},
),
IconButton(
icon: Icon(Icons.bluetooth_disabled), iconSize: 30,
onPressed: () {
_bluetoothProvider.disconnectFromDevice();
print(_bluetoothProvider.bluetoothState.toString());
},
),
],
),
body: joystickWidget(),
);
});
}
}
}
context.read<BluetoothProvider>().startScan(); is a shortcut for Provider.of<BluetoothProvider>(context, listen: false).startScan() : it basically does the same thing.
Related
I'm working on geolocation with geolocation 7.6.2 package. I have a problem with memory leak when closing the stream. Widget looks like this:
class _GeolocatorActiveState extends State<GeolocatorActive> {
StreamSubscription<Position>? _currentPosition;
double distanceToday = 0.0;
double distanceTotal = 0.0;
bool gpsActive = true;
#override
void initState() {
getCurrentPosition();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
// Some stuff
),
),
backgroundColor: Colors.deepOrange
),
body: Column(
children: [
// some stuff here
TextButton(onPressed: () {
setState(() {
gpsActive = false;
stopStream();
});
Navigator.pushNamed(context, '/');},
child: Text(
'Finish',
)
),
// Some stuff there
],
),
);
}
void getCurrentPosition () {
final positionStream = GeolocatorPlatform.instance.getPositionStream();
if(gpsActive == true){
_currentPosition = positionStream.listen((position) {
setState(() {
long = position.longitude.toString();
lat = position.latitude.toString();
});
});
}
}
void stopStream () {
_currentPosition = null;
_currentPosition?.cancel();
super.dispose();
}
}
Now, the thing is that when I push the button "Finish" I want to close and remove this Widget. I tried with Navigator.pushNamedAndRemoveUntil and thought it causes the memory leak but no. I had stopStream function inside getCurrentPosition as else statement but it didn't work either. How can I force app to close stream before it closes this widget? Or am I missing something?
Error looks like this:
E/flutter ( 4655): [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: setState() called after dispose(): _GeolocatorActiveState#6b088(lifecycle state: defunct)
E/flutter ( 4655): This error happens if you call setState() on a State object for a widget that no longer appears in the widget tree (e.g., whose parent widget no longer includes the widget in its build). This error can occur when code calls setState() from a timer or an animation callback.
E/flutter ( 4655): The preferred solution is to cancel the timer or stop listening to the animation in the dispose() callback. Another solution is to check the "mounted" property of this object before calling setState() to ensure the object is still in the tree.
E/flutter ( 4655): This error might indicate a memory leak if setState() is being called because another object is retaining a reference to this State object after it has been removed from the tree. To avoid memory leaks, consider breaking the reference to this object during dispose().
So there are two things wrong here:
You call super.dispose outside of dispose method, which is not something you should do.
You nullify _currentComposition stream first and then you try to cancel it, which is too late because you already lost access to that stream. You should switch the order.
By the way, I think you can easily put all stream disposal method inside dispose, rather than close them on button't onTap callback.
Here is your code example that I modified, notice overriden dispose method:
class _GeolocatorActiveState extends State<GeolocatorActive> {
StreamSubscription<Position>? _currentPosition;
double distanceToday = 0.0;
double distanceTotal = 0.0;
bool gpsActive = true;
#override
void initState() {
super.initState();
getCurrentPosition();
}
#override
void dispose() {
_currentPosition?.cancel();
_currentPosition = null;
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
// Some stuff
),
),
backgroundColor: Colors.deepOrange
),
body: Column(
children: [
// some stuff here
TextButton(onPressed: () {
setState(() {
gpsActive = false;
});
Navigator.pushNamed(context, '/');},
child: Text(
'Finish',
)
),
// Some stuff there
],
),
);
}
void getCurrentPosition () {
final positionStream = GeolocatorPlatform.instance.getPositionStream();
if(gpsActive == true){
_currentPosition = positionStream.listen((position) {
setState(() {
long = position.longitude.toString();
lat = position.latitude.toString();
});
});
}
}
}
You can try what it suggests:
Another solution is to check the "mounted" property of this object before calling setState() to ensure the object is still in the tree.
Like this:
if (mounted) {
setState(() {
long = position.longitude.toString();
lat = position.latitude.toString();
});
}
I'm calling a API and saving its response using providers so that i can access from anywhere in my flutter app. The model used for storing response is
class Album with ChangeNotifier {
final String id;
final List<String> categoriesId;
final String title;
final String imageUrl;
Album({this.id, this.categoriesId, this.title, this.imageUrl});
factory Album.fromJson(Map<String, dynamic> json) {
List<dynamic> res = json['category_list'];
List<String> cats = [];
res.forEach((tx) {
cats.add(tx.toString());
});
return Album(
id: json['id'].toString(),
categoriesId: cats,
title: json['name'],
imageUrl: json["image"],
);
}
}
While the function calling endpoint is,
String albumUrl = "http://192.168.227.102:9000/static/album.json";
Future<List<Album>> fetchAnime(BuildContext context) async {
List<Album> resAlbum = [];
try {
final response = await http.get(Uri.parse(albumUrl));
if (response.statusCode == 200) {
List<dynamic> result = jsonDecode(response.body)['album'];
result.forEach((tx) {
Album test = Anime.fromJson(tx as Map<String, dynamic>);
resAlbum.add(test);
});
}
} catch (e) {
print(e);
}
return resAlbum;
}
The provider for model Album is,
class AlbumProvider with ChangeNotifier {
List<Album> _items = [];
void getPostData(context) async {
bool loading = true;
if (this._items.isNotEmpty) {
this._items = await fetchAlbum(context);
}
loading = false;
notifyListeners();
}
}
The view using this provider is,
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
void initState() {
Provider.of<AlbumProvider>(context, listen: false).getPostData(context);
super.initState();
}
#override
Widget build(BuildContext context) {
List<Album> albumData = Provider.of<AlbumProvider>(context).allitems;
return Scaffold(
appBar: AppBar(
title: Text(
'Home Screen',
),
),
body:albumData.isEmpty
?Text("we are fetching data")
:Text("fetching complete")
)
}
}
When i run the app i got following error,
The following assertion was thrown while dispatching notifications for AlbumProvider:
setState() or markNeedsBuild() called during build.
This _InheritedProviderScope widget cannot be marked as needing to build because the framework is already in the process of building widgets. A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase.
The widget on which setState() or markNeedsBuild() was called was: _InheritedProviderScope
What are the reasons for getting the error and How can i remove this error?
This is because, when you are calling
Provider.of<AlbumProvider>(context, listen: false).getPostData(context);
from initState method of HomePage, which in turn invokes following method
void getPostData(context) async {
bool loading = true;
if (this._items.isNotEmpty) {
this._items = await fetchAlbum(context);
}
loading = false;
notifyListeners();
}
This function is calling notifiyListeners();
The issue is that you are listening for changes to AlbumProvider in build method of HomePage.
List<Album> albumData = Provider.of<AlbumProvider>(context).allitems;
So ultimately, initState is calling getPostData which is calling notifyListeners() which will try to rebuild the widget tree before it has finished building.
You should FutureBuilder instead of calling getPostData() in the initState method.
I have isolate that makes some heavy calculations then on receive the list with the result run a for loop to add them to observable list with items var items = [].obs;
The thing is I'm trying to observe the items list from a splash controller and once the list != [] I'll navigate to another screen, so in onInit() I have this code:
class SplashController extends GetxController {
#override
void onInit() {
final ItemsController _itemsController = Get.put(ItemsController());
// TODO: implement onInit
super.onInit();
ever(_itemsController.items, (newItems) {
print('new items here $newItems');
});
}
}
Despite the itemsController.items is populated (after the for loop I print the itemsController.items and it's not empty) the worker on the splash controller doesn't trigger when the items are added.
What am I doing wrong here? Is this the correct way to observe variable outside of widget using Getx?
Can anyone help me with this, please?
Edit: In the items controller I’m adding the items this way
add(item) => items.add(item)
Continuing with the Isolate example, but without using a StatefulWidget i.e. no setState usage.
The ever worker in SplashX will receive items generated from the Isolate. The Stateless Widget page will display the latest item emitted from the Isolate.
SplashController + ever worker
class SplashX extends GetxController {
ItemsX itemsX;
SplashX({this.itemsX});
#override
void onInit() {
super.onInit();
ever(itemsX.items, (items) => print('Ever items: $items'));
}
}
Items Controller
class ItemsX extends GetxController {
RxList<String> items = RxList<String>();
bool running = false;
void add(String item) {
items.add(item);
}
void updateStatus(bool isRunning) {
running = isRunning;
update();
}
void reset() {
items.clear();
}
/// Only relevant for UnusedControllerPage
List<Widget> get texts => items.map((item) => Text('$item')).toList();
}
Isolate Controller
class IsolateX extends GetxController {
IsolateX({this.itemsX});
ItemsX itemsX;
Isolate _isolate;
static int _counter = 0;
ReceivePort _receivePort;
bool running = false;
static void _checkTimer(SendPort sendPort) async {
Timer.periodic(Duration(seconds: 1), (Timer t) {
_counter++;
String msg = 'notification ' + _counter.toString();
print('SEND: ' + msg);
sendPort.send(msg);
});
}
void _handleMessage(dynamic data) {
itemsX.add(data); // update observable
}
void updateStatus(bool isRunning) {
running = isRunning;
update();
}
void start() async {
itemsX.reset();
updateStatus(true);
_receivePort = ReceivePort();
_isolate = await Isolate.spawn(_checkTimer, _receivePort.sendPort);
_receivePort.listen(_handleMessage, onDone:() {
print("done!");
});
}
void stop() {
if (_isolate != null) {
updateStatus(false);
_receivePort.close();
_isolate.kill(priority: Isolate.immediate);
_isolate = null;
}
}
}
Stateless Page
class MyHomePageStateless extends StatelessWidget {
#override
Widget build(BuildContext context) {
ItemsX ix = Get.put(ItemsX()); // Instantiate ItemsController
IsolateX isox = Get.put(IsolateX(itemsX: ix));
SplashX sx = Get.put(SplashX(itemsX: ix));
return Scaffold(
appBar: AppBar(
title: Text('Isolate Stateless'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
GetX<ItemsX>(
builder: (ix) => Text(ix.items.isNotEmpty ? ix.items.last : ''),
),
],
),
),
floatingActionButton: GetBuilder<IsolateX>(
builder: (_ix) => FloatingActionButton(
onPressed: _ix.running ? isox.stop : isox.start,
tooltip: _ix.running ? 'Timer stop' : 'Timer start',
child: _ix.running ? Icon(Icons.stop) : Icon(Icons.play_arrow),
),
),
);
}
}
Here's two controllers, with one ever worker listening for events of another controller, where that controller's events are coming from data generated in an Isolate.
I'm not aware of anything special about generating data in an Isolate as opposed to any other async data source, but I'm not overly familiar with Isolates.
Controllers
class SplashX extends GetxController {
ItemsX itemsX;
SplashX({this.itemsX});
#override
void onInit() {
super.onInit();
ever(itemsX.items, (items) => print('Received items: $items'));
}
}
class ItemsX extends GetxController {
RxList<String> items = RxList<String>();
void add(String item) {
items.add(item);
}
/// Only relevant for SimplePage at bottom
List<Widget> get texts => items.map((item) => Text('$item')).toList();
}
Page /w Isolate
And here's the edits to the Isolate snippet which you're using.
I've instantiated ItemsX controller as a field and SplashX in onInit.
(There shouldn't be a need to use Stateful Widgets since you can put all state into a Controller, but I didn't want to rewrite the Isolate example).
class _MyHomePageState extends State<MyHomePage> {
Isolate _isolate;
bool _running = false;
static int _counter = 0;
String notification = "";
ReceivePort _receivePort;
ItemsX ix = Get.put(ItemsX()); // Instantiate ItemsController
#override
void initState() {
super.initState();
SplashX sx = Get.put(SplashX(itemsX: ix));
// ↑ Instantiate SplashCont with ever worker
}
Change to the _handleMessage method:
void _handleMessage(dynamic data) {
//print('RECEIVED: ' + data);
ix.add(data); // update observable
setState(() {
notification = data;
});
}
And finally the debug output results showing ever worker handling observable events (Received items...) :
[GETX] "ItemsX" has been initialized
[GETX] "SplashX" has been initialized
I/flutter (19012): SEND: notification 1
I/flutter (19012): Received items: [notification 1]
I/flutter (19012): SEND: notification 2
I/flutter (19012): Received items: [notification 1, notification 2]
I/flutter (19012): SEND: notification 3
I/flutter (19012): Received items: [notification 1, notification 2, notification 3]
I/flutter (19012): done!
Controllers in Non-Isolate Page
Example of using the same controllers above, without the noise of a Stateful Widget page and all the Isolate stuff.
class SplashX extends GetxController {
ItemsX itemsX;
SplashX({this.itemsX});
#override
void onInit() {
super.onInit();
ever(itemsX.items, (items) => print('Received items: $items'));
}
}
class ItemsX extends GetxController {
RxList<String> items = RxList<String>();
void add(String item) {
items.add(item);
}
/// Only relevant for SimplePage
List<Widget> get texts => items.map((item) => Text('$item')).toList();
}
class SimplePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
ItemsX ix = Get.put(ItemsX());
SplashX sx = Get.put(SplashX(itemsX: ix));
return Scaffold(
body: SafeArea(
child: Column(
children: [
Expanded(
flex: 10,
child: Obx(
() => ListView(
children: ix.texts,
),
),
),
Expanded(
flex: 1,
child: RaisedButton(
child: Text('Add'),
onPressed: () => ix.add('more...'),
)
)
],
),
),
);
}
}
I need help regarding the images in my app. I would like to add 3 buttons for:
Download
Save as
Change phone wallpaper
I'm not using urls. I already have my images in an assets repository.
Do you have any idea how I can do that? Thank you.
You can copy paste run full code below
You can use package https://pub.dev/packages/wallpaper_manager
You can directly set wallpaper with image in assets
Example code's assets image path is "assets/tmp1.jpg"
code snippet
result = await WallpaperManager.setWallpaperFromAsset(
assetPath, WallpaperManager.HOME_SCREEN);
working demo
full code
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:wallpaper_manager/wallpaper_manager.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String _platformVersion = 'Unknown';
String _wallpaperFile = 'Unknown';
String _wallpaperAsset = 'Unknown';
#override
void initState() {
super.initState();
}
// Platform messages are asynchronous, so we initialize in an async method.
Future<void> initPlatformState() async {
String platformVersion;
// Platform messages may fail, so we use a try/catch PlatformException.
try {
platformVersion = await WallpaperManager.platformVersion;
} on PlatformException {
platformVersion = 'Failed to get platform version.';
}
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
setState(() {
_platformVersion = platformVersion;
});
}
// Platform messages are asynchronous, so we initialize in an async method.
Future<void> setWallpaperFromFile() async {
setState(() {
_wallpaperFile = "Loading";
});
String result;
var file = await DefaultCacheManager().getSingleFile(
'https://images.unsplash.com/photo-1542435503-956c469947f6');
// Platform messages may fail, so we use a try/catch PlatformException.
try {
result = await WallpaperManager.setWallpaperFromFile(
file.path, WallpaperManager.HOME_SCREEN);
} on PlatformException {
result = 'Failed to get wallpaper.';
}
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
setState(() {
_wallpaperFile = result;
});
}
// Platform messages are asynchronous, so we initialize in an async method.
Future<void> setWallpaperFromAsset() async {
setState(() {
_wallpaperAsset = "Loading";
});
String result;
String assetPath = "assets/tmp1.jpg";
// Platform messages may fail, so we use a try/catch PlatformException.
try {
result = await WallpaperManager.setWallpaperFromAsset(
assetPath, WallpaperManager.HOME_SCREEN);
} on PlatformException {
result = 'Failed to get wallpaper.';
}
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
setState(() {
_wallpaperAsset = result;
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: Column(
children: <Widget>[
RaisedButton(
child: Text("Platform Version"),
onPressed: initPlatformState,
),
Center(
child: Text('Running on: $_platformVersion\n'),
),
RaisedButton(
child: Text("Set wallpaper from file"),
onPressed: setWallpaperFromFile,
),
Center(
child: Text('Wallpaper status: $_wallpaperFile\n'),
),
RaisedButton(
child: Text("Set wallpaper from asset"),
onPressed: setWallpaperFromAsset,
),
Center(
child: Text('Wallpaper status: $_wallpaperAsset\n'),
),
],
)),
);
}
}
in flutter i just learn how can i use Bloc on applications and i want to try to implementing simple login with this feature. after implementing some class of bloc to using that on view
i get error when i try to use this code as
BlocProvider.of<LoginListingBloc>(context).dispatch(LoginEvent(loginInfoModel: testLogin));
inside RaisedButton
Error:
BlocProvider.of() called with a context that does not contain a Bloc
of type LoginListingBloc.
My view :
class _HomePageState extends State<HomePage> {
LoginListingBloc _loginListingBloc;
#override
void initState() {
super.initState();
_loginListingBloc =
LoginListingBloc(loginRepository: widget.loginRepository);
}
...
#override
Widget build(BuildContext context) {
return BlocProvider(
bloc: _loginListingBloc,
child: Scaffold(
appBar: AppBar(
elevation: 5.0, title: Text('Sample Code', style: appBarTextStyle)),
body: Center(
child: RaisedButton(
child: Text(
'click here',
style: defaultButtonStyle,
),
onPressed: () {
BlocProvider.of<LoginListingBloc>(context).dispatch(LoginEvent(loginInfoModel: testLogin));
}),
),
),
);
}
}
LoginListingBloc class:
class LoginListingBloc extends Bloc<LoginListingEvent, LoginListingStates> {
final LoginRepository loginRepository;
LoginListingBloc({this.loginRepository});
#override
LoginListingStates get initialState => LoginUninitializedState();
#override
Stream<LoginListingStates> mapEventToState(
LoginListingStates currentState, LoginListingEvent event) async* {
if (event is LoginEvent) {
yield LoginFetchingState();
try {
final loginInfo = await loginRepository.fetchLoginToPage(
event.loginInfoModel.username, event.loginInfoModel.password);
yield LoginFetchedState(userInfo: loginInfo);
} catch (_) {
yield LoginErrorState();
}
}
}
}
and other classes if you want to see theme
AppApiProvider class:
class AppApiProvider {
final successCode = 200;
Future<UserInfo> fetchLoginToPage(String username, String password) async {
final response = await http.get(Constants.url + "/api/v1/getPersons");
final responseString = jsonDecode(response.body);
if (response.statusCode == successCode) {
print(responseString);
return UserInfo.fromJson(responseString);
} else {
throw Exception('failed to get information');
}
}
}
LoginEvent:
class LoginEvent extends LoginListingEvent {
final LoginInfoModel loginInfoModel;
LoginEvent({#required this.loginInfoModel}) : assert(loginInfoModel != null);
}
LoginInfoModel:
class LoginInfoModel {
String username;
String password;
LoginInfoModel({this.username, this.password});
}
final testLogin = LoginInfoModel(username:'exmaple',password:'text');
No need to access loginListingBloc from context since it exists in the current class and not up the widget tree.
change:
BlocProvider.of<LoginListingBloc>(context).dispatch(LoginEvent(loginInfoModel: testLogin));
to:
_loginListingBloc.dispatch(LoginEvent(loginInfoModel: testLogin));
For all others who come here for the error message:
Make sure you always specify the types and don't omit them:
BlocProvider<YourBloc>(
create: (context) => YourBloc()
child: YourWidget()
);
and also for
BlocProvider.of<YourBloc>(context);