I use https://pub.dev/packages/localstorage to create local storage in my Flutter app.
I created some method to use with this local storage in this 'Event' widget
class Event extends StatefulWidget {
final eventInfo;
Event({Key key, List eventInfo})
: this.eventInfo = eventInfo,
super(key: key);
#override
_EventState createState() => _EventState();
}
class FavName {
String eventName;
FavName({this.eventName});
get favEventName {
return eventName;
}
toJSONEncodable() {
Map<String, dynamic> m = new Map();
m['Name'] = eventName;
return m;
}
}
class FavList {
List<FavName> favNameList;
FavList() {
favNameList = new List();
}
toJSONEncodable() {
return favNameList.map((item) {
return item.toJSONEncodable();
}).toList();
}
}
class _EventState extends State<Event> {
final FavList list = new FavList();
final LocalStorage storage = new LocalStorage('favList');
addItem(String eventName) {
setState(() {
final item = new FavName(eventName: eventName);
list.favNameList.add(item);
_saveToStorage();
});
}
deleteItem(String eventName) {
setState(() {
final item = new FavName(eventName: eventName);
list.favNameList.remove(item);
_saveToStorage();
});
}
_saveToStorage() {
storage.setItem('favList', list.toJSONEncodable());
}
. . .
So I wanted to add a String into my local storage with a button.
Here is my code for the button.
child: IconButton(
icon: Icon(
Icons.star,
color: _iconColors,
),
onPressed: () {
print(name);
(storage.getItem(name)) ? deleteItem(name)
: addItem(name);
setState(() {
if (storage.getItem(name) == null) {
_iconColors = Colors.grey;
} else {
_iconColors = Colors.yellow;
}
});
})
But when I tap on the button, I get this error
I/flutter (27489): 2-Art Event //Print the string that I want to add to the local storage
════════ Exception caught by gesture ═══════════════════════════════════════════
The following assertion was thrown while handling a gesture:
Failed assertion: boolean expression must not be null
When the exception was thrown, this was the stack
#0 _EventState.buildInfo.<anonymous closure>
package:lx_building/screens/event.dart:190
#1 _InkResponseState._handleTap
package:flutter/…/material/ink_well.dart:772
#2 _InkResponseState.build.<anonymous closure>
package:flutter/…/material/ink_well.dart:855
#3 GestureRecognizer.invokeCallback
package:flutter/…/gestures/recognizer.dart:182
#4 TapGestureRecognizer.handleTapUp
package:flutter/…/gestures/tap.dart:522
...
Handler: "onTap"
Recognizer: TapGestureRecognizer#1f697
debugOwner: GestureDetector
state: possible
won arena
finalPosition: Offset(324.7, 264.3)
finalLocalPosition: Offset(32.4, 9.7)
button: 1
sent tap down
════════════════════════════════════════════════════════════════════════════════
How can I fix this?
storage.getItem(name) returns the data stored which generally is a Map.
In your condition you must use Boolean. You can just test that your return isn't null :
(storage.getItem(name) != null) ? deleteItem(name)
: addItem(name);
Related
I am currently working on this tutorial in which transfers a variable to an external file to another (with a callback)
but i have this error :
======== Exception caught by gesture ===============================================================
The following NoSuchMethodError was thrown while handling a gesture:
The method 'call' was called on null.
Receiver: null
Tried calling: call(3)
When the exception was thrown, this was the stack:
#0 Object.noSuchMethod (dart:core-patch/object_patch.dart:54:5)
#1 Son.build.<anonymous closure> (package:montest/son.dart:24:21)
#2 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:182:24)
#3 TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:607:11)
#4 BaseTapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:296:5)
...
Handler: "onTap"
Recognizer: TapGestureRecognizer#cf4d9
debugOwner: GestureDetector
state: possible
won arena
finalPosition: Offset(178.3, 84.9)
button: 1
sent tap down
====================================================================================================
son.dart
///
/// Inside the Child Widget
///
import 'package:flutter/material.dart';
// Step 1: Define a Callback.
typedef void IntCallback(int id);
class Son extends StatelessWidget {
// Step 2: Configre the child to expect a callback in the constructor(next 2 lines):
final IntCallback onSonChanged;
Son({ #required this.onSonChanged });
int elementId = 3;
#override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () {
// Step 3: On specific action(e.g onPressed/
// onTap/onLoad.. onWhatEver) trigger the callback
// with the data you want to pass to the parent.
// Data will be passed as parameter(see elementId):
onSonChanged(elementId);
// Done in the child.
},
child: Text('Click me to call the callback!'),
);
}
}
///
///////////////
parent.dart
import 'son.dart';
///
/// Inside the Parent Widget
///
import 'package:flutter/material.dart';
class Father extends StatefulWidget {
#override
_FatherState createState() => _FatherState();
}
class _FatherState extends State<Father> {
// Step 1 (optional): Define a Global variable
// to store the data comming back from the child.
int id;
// Step 2: Define a function with the same signature
// as the callback, so the callback will point to it,
// this new function will get the data from the child,
// set it to the global variable (from step 1)
// in the parent, and then update the UI by setState((){});
void updateId(int newId) {
setState(() {
id = newId;
});
}
#override
Widget build(BuildContext context) {
return Container(
// Step 3: Construct a child widget and pass the
child: Son(
// Many options to make onSonChanged points to
// an executable code(function) within memory
// called 'updateId':
//
// 1st option:
onSonChanged: (int newId) {
updateId(newId);
},
// 2nd option: onSonChanged: updateId,
// 3rd option: onSonChanged: (int newId) => updateId(newId)
// So each time the 'onSonChanged' called by the action
// we defined inside the child, a new data will be
// passed to this parent.
)
);
}
}
Thanks for your help
make id nullable and check it.
try this
import 'package:flutter/material.dart';
typedef void IntCallback(int id);
class Son extends StatelessWidget {
final IntCallback onSonChanged;
Son({required this.onSonChanged});
int elementId = 3;
#override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () {
onSonChanged(elementId);
},
child: Text('Click me to call the callback!'),
);
}
}
class Father extends StatefulWidget {
#override
_FatherState createState() => _FatherState();
}
class _FatherState extends State<Father> {
int? id;
void updateId(int newId) {
setState(() {
id = newId;
});
}
#override
Widget build(BuildContext context) {
return Column(
children: [
id == null ? Text(" son value: null") : Text("son value: $id"),
Container(
child: Son(
onSonChanged: (int newId) {
updateId(newId);
},
),
),
],
);
}
}
Context
I have this AppUser class:
#immutable
class AppUser {
const AppUser({
this.displayName,
this.email,
required this.emailVerified,
this.phoneNumber,
this.photoURL,
required this.uid,
});
AppUser.fromFirebaseUser(User user)
: displayName = user.displayName,
email = user.email,
emailVerified = user.emailVerified,
phoneNumber = user.phoneNumber,
photoURL = user.photoURL,
uid = user.uid;
final String? displayName;
final String? email;
final bool emailVerified;
final String? phoneNumber;
final String? photoURL;
final String uid;
}
In order to manage and use the current user signed in, I have this AppUserController class:
class AppUserController extends StateNotifier<AppUser> {
AppUserController()
: super(
const AppUser(
emailVerified: false,
uid: '',
),
);
Stream<User?> get onAuthStateChanges =>
FirebaseAuth.instance.authStateChanges();
set setAppUser(AppUser appUser) {
state = appUser;
}
Future<void> signOut() async {
await FirebaseAuth.instance.signOut();
}
}
Then, I created 2 providers:
final appUserProvider =
StateNotifierProvider<AppUserController, AppUser>((ref) {
return AppUserController();
});
final appUserStreamProvider = StreamProvider<AppUser?>((ref) {
return ref
.read(appUserProvider.notifier)
.onAuthStateChanges
.map<AppUser?>((user) {
return user != null ? AppUser.fromFirebaseUser(user) : null;
});
});
I need to manage a user’s budgets list. Also, I have to synchronize this list with a Cloud Firestore database, so I created the BudgetsService class:
class BudgetsService {
BudgetsService({
required this.uid,
}) : budgetsRef = FirebaseFirestore.instance
.collection(FirestorePath.budgetsCollection(uid))
.withConverter<Budget>(
fromFirestore: (snapshot, _) => Budget.fromMap(snapshot.data()!),
toFirestore: (budget, _) => budget.toMap(),
);
String uid;
final CollectionReference<Budget> budgetsRef;
Future<void> addUpdate(Budget budget) async {
await budgetsRef.doc(documentPath(budget)).set(budget);
}
Future<void> delete(Budget budget) async {
await budgetsRef.doc(documentPath(budget)).delete();
}
String documentPath(Budget budget) => FirestorePath.budgetDoc(uid, budget);
Future<List<Budget>> getBudgets() async {
final list = await budgetsRef.get();
return list.docs.map((e) => e.data()).toList();
}
}
I use this class through budgetsServiceProvider provider:
final budgetsServiceProvider = Provider<BudgetsService>((ref) {
final AppUser appUser = ref.watch(appUserProvider);
final String uid = appUser.uid;
return BudgetsService(uid: uid);
});
I use BudgetsService class only to interact with the online database. For the rest, I manage the user’s budget list with BudgetsController class:
class BudgetsController extends StateNotifier<List<Budget>> {
BudgetsController() : super(<Budget>[]);
List<String> get names => state.map((b) => b.name).toList();
Future<void> addUpdate(Budget budget, BudgetsService budgetsService) async {
await budgetsService.addUpdate(budget);
if (budgetAlreadyExists(budget)) {
final int index = indexOf(budget);
final List<Budget> newState = [...state];
newState[index] = budget;
state = newState..sort();
} else {
state = [...state, budget]..sort();
}
}
bool budgetAlreadyExists(Budget budget) => names.contains(budget.name);
Future<void> delete(Budget budget, BudgetsService budgetsService) async {
await budgetsService.delete(budget);
final int index = indexOf(budget);
if (index != -1) {
final List<Budget> newState = [...state]
..removeAt(index)
..sort();
state = newState;
}
}
Future<void> retrieveBudgets(BudgetsService budgetsService) async {
state = await budgetsService.getBudgets();
}
int indexOf(Budget budget) => state.indexWhere((b) => b.name == budget.name);
}
I use this class through budgetsProvider provider:
final budgetsProvider =
StateNotifierProvider<BudgetsController, List<Budget>>((ref) {
return BudgetsController();
});
After the user is signed in, my SwitchScreen widget navigates to ConsoleScreen:
class SwitchScreen extends HookWidget {
const SwitchScreen({
Key? key,
}) : super(key: key);
static const route = '/switch';
#override
Widget build(BuildContext context) {
final appUserStream =
useProvider<AsyncValue<AppUser?>>(appUserStreamProvider);
final googleSignIn =
useProvider<GoogleSignInService>(googleSignInServiceProvider);
final appUserController =
useProvider<AppUserController>(appUserProvider.notifier);
return appUserStream.when(
data: (data) {
if (data != null) {
appUserController.setAppUser = data;
final budgetsService = useProvider(budgetsServiceProvider);
return const ConsoleScreen();
} else {
return SignInScreen(
onGooglePressed: googleSignIn.signInWithGoogle,
);
}
},
loading: () {
return const Scaffold(
body: Center(
child: LinearProgressIndicator(),
),
);
},
error: (error, stack) {
return Scaffold(
body: Center(
child: Text('Error: $error'),
),
);
},
);
}
}
Problem
The first time I build the app, I have no problem. But when I perform the hot reload, I get the following error message:
══════ Exception caught by widgets library ═══════════════════════════════════
The following Error was thrown building SwitchScreen(dirty, dependencies: [UncontrolledProviderScope], AsyncValue<AppUser?>.data(value: Instance of 'AppUser'), Instance of 'GoogleSignInService', Instance of 'AppUserController'):
Instance of 'Error'
The relevant error-causing widget was
SwitchScreen
lib\main.dart:67
When the exception was thrown, this was the stack
#0 StateNotifier.state=
package:state_notifier/state_notifier.dart:173
#1 AppUserController.setAppUser=
package:financesmanager/controllers/app_user_controller.dart:42
#2 SwitchScreen.build.<anonymous closure>
package:financesmanager/screens/switch_screen.dart:33
#3 _$AsyncData.when
package:riverpod/src/common.freezed.dart:148
#4 SwitchScreen.build
package:financesmanager/screens/switch_screen.dart:28
...
════════════════════════════════════════════════════════════════════════════════
E/flutter (13932): [ERROR:flutter/shell/common/shell.cc(103)] Dart Unhandled Exception: setState() or markNeedsBuild() called during build.
E/flutter (13932): This UncontrolledProviderScope 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.
E/flutter (13932): The widget on which setState() or markNeedsBuild() was called was:
E/flutter (13932): UncontrolledProviderScope
E/flutter (13932): The widget which was currently being built when the offending call was made was:
E/flutter (13932): SwitchScreen, stack trace: #0 Element.markNeedsBuild.<anonymous closure>
package:flutter/…/widgets/framework.dart:4217
E/flutter (13932): #1 Element.markNeedsBuild
package:flutter/…/widgets/framework.dart:4232
E/flutter (13932): #2 ProviderElement._debugMarkWillChange.<anonymous closure>
package:riverpod/…/framework/base_provider.dart:660
E/flutter (13932): #3 ProviderElement._debugMarkWillChange
package:riverpod/…/framework/base_provider.dart:664
E/flutter (13932): #4 ProviderStateBase.exposedValue=.<anonymous closure>
package:riverpod/…/framework/base_provider.dart:900
E/flutter (13932): #5 ProviderStateBase.exposedValue=
package:riverpod/…/framework/base_provider.dart:902
E/flutter (13932): #6 _StateNotifierProviderState._listener
package:riverpod/src/state_notifier_provider.dart:92
E/flutter (13932): #7 StateNotifier.state=
package:state_notifier/state_notifier.dart:162
E/flutter (13932): #8 AppUserController.setAppUser=
package:financesmanager/controllers/app_user_controller.dart:42
E/flutter (13932): #9 SwitchScreen.build.<anonymous closure>
package:financesmanager/screens/switch_screen.dart:33
Question
How can I solve the problem?
Thank you very much!
Update (2021-06-08)
In my main.dart file I have:
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Hive.initFlutter();
runApp(ProviderScope(child: FMApp()));
}
class FMApp extends HookWidget {
FMApp({
Key? key,
}) : super(key: key);
final Future<FirebaseApp> _initialization = Firebase.initializeApp();
#override
Widget build(BuildContext context) {
final darkTheme = AppTheme.theme(Brightness.dark);
final lightTheme = AppTheme.theme(Brightness.light);
final isLightTheme = useProvider<bool>(themePreferenceProvider);
final theme = isLightTheme ? lightTheme : darkTheme;
return FutureBuilder(
future: _initialization,
builder: (context, snapshot) {
if (snapshot.hasError) {
return FlutterFireInitErrorScreen(
appTitle: 'FM App',
darkTheme: darkTheme,
error: snapshot.error,
theme: theme,
);
}
if (snapshot.connectionState == ConnectionState.done) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'FM App',
localizationsDelegates: const [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: const [
Locale.fromSubtags(languageCode: 'en'),
Locale.fromSubtags(languageCode: 'es'),
Locale.fromSubtags(languageCode: 'it'),
],
darkTheme: darkTheme,
theme: theme,
initialRoute: SwitchScreen.route,
routes: {
SwitchScreen.route: (context) => const SwitchScreen(),
},
);
}
return FlutterFireInitWaitingScreen(
appTitle: 'FM App',
darkTheme: darkTheme,
theme: theme,
);
},
);
}
}
Possible solution
For now I solved it by replacing, in switch_screen.dart file, this code:
final budgetsService = useProvider(budgetsServiceProvider);
final budgetsController = context.read<BudgetsController>(budgetsProvider.notifier);
budgetsController.retrieveBudgets(budgetsService);
with the following:
final budgetsService = BudgetsService(uid: data.uid);
context
.read(budgetsControllerProvider)
.retrieveBudgets(budgetsService);
What do you think? Is this a good solution? Is there a better one? Thank you!
The interpretation of the error is that two widgets are updating at the same time, probably because they watch the same provider.
When a Child Widget tries to rebuild while its Parent Widget also tries to rebuild, it generates this error. To solve this error, only the Parent Widget needs to rebuild, because the Child Widget will automatically rebuild.
Unfortunately, in the code you provide, I cannot see from where your SwitchScreen is displayed so I cannot tell you where the exact problem could be.
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.
I am noob with flutter and dart. While trying out flutter provider state management package I am getting the following error:
[ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: Looking up a deactivated widget's ancestor is unsafe.
At this point the state of the widget's element tree is no longer stable.
To safely refer to a widget's ancestor in its dispose() method, save a reference to the ancestor by calling dependOnInheritedWidgetOfExactType() in the widget's didChangeDependencies() method.
E/flutter (23887): #11 _rootRun (dart:async/zone.dart:1184:13)
E/flutter (23887): #12 _CustomZone.run (dart:async/zone.dart:1077:19)
E/flutter (23887): #13 _CustomZone.bindCallback.<anonymous closure> (dart:async/zone.dart:1003:23)
E/flutter (23887): #14 Timer._createTimer.<anonymous closure> (dart:async-patch/timer_patch.dart:23:15)
E/flutter (23887): #15 _Timer._runTimers (dart:isolate-patch/timer_impl.dart:398:19)
E/flutter (23887): #16 _Timer._handleMessage (dart:isolate-patch/timer_impl.dart:429:5)
E/flutter (23887): #17 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:168:12)
What Am I trying to do?
I want to create a tab bar. The tab items will be fetched from the server dynamically. Then I will click on the tabs and they will load user feed posts by category id. If I slide between tabs it does not throw any error. If I tap the tab items one by one, it does not throw any error. but if I tap on the 2nd next item it throws the error specified above. Here is My Code
FeedListScreen.dart:
import 'package:flutter/material.dart';
import 'package:flutter_x/Providers/FeedProvider.dart';
import 'package:flutter_x/Widgets/FeedList.dart';
import 'package:provider/provider.dart';
class FeedListScreen extends StatefulWidget {
#override
_FeedListScreenState createState() => _FeedListScreenState();
}
class _FeedListScreenState extends State<FeedListScreen> {
bool _canLoadTabs = false;
List<Tab> _tabs = [];
List<Widget> _tabContainers = [];
#override
void initState() {
Future.delayed(Duration(seconds: 0), () {
var fp = Provider.of<FeedProvider>(context, listen: false);
fp.fetchFeedTypes().then((value) {
for (int i = 0; i < fp.feedTypeList.length; i++) {
_tabs.add(Tab(text: fp.feedTypeList[i].name,));
_tabContainers.add(FeedList(fp.feedTypeList[i].id));
}
setState(() {
_canLoadTabs = true;
});
}
);
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Consumer<FeedProvider>(
builder: (ctx, feedProvider, _) {
if (!_canLoadTabs) {
return Center(child: CircularProgressIndicator());
}
return DefaultTabController(
length: _tabs.length,
child: Scaffold(
appBar: AppBar(
bottom: TabBar(
tabs: _tabs,
isScrollable: true,
),
),
body: TabBarView(
children: _tabContainers,
),
)
);
},
);
}
}
FeedList.dart
import 'package:flutter/material.dart';
import 'package:flutter_x/Providers/FeedProvider.dart';
import 'package:provider/provider.dart';
class FeedList extends StatelessWidget {
String _id;
FeedList([String id]) {
_id = id;
}
#override
Widget build(BuildContext context) {
Future.delayed(Duration(seconds: 0), () {
if (_id == null) {
print('_id is null.. not fetching');
} else {
Provider.of<FeedProvider>(context, listen: false).fetchFeedList(page: 1, feedId: _id); //<= This line causing the error
}
});
return ListView.builder(
shrinkWrap: true,
itemCount: 20, //dummy
itemBuilder: (ctx, index) => ListTile(
leading: CircleAvatar(
child: Text('$index'),
),
),
);
}
}
FeedProvider.dart
import 'package:flutter/material.dart';
class FeedProvider with ChangeNotifier {
List<FeedType> _feedTypes = [];
List<FeedItem> _feedItemList = [];
///getters
List<FeedType> get feedTypeList => _feedTypes;
List<FeedItem> get feedList => _feedItemList;
.... other getters...
///setters
... other setters...
set feedTypeList(List<FeedType> ft) {
if (_feedTypes != ft) {
_feedTypes = ft;
notifyListeners();
}
}
set feedList(List<FeedItem> fl) {
if(fl.length == 0){
_feedItemList = [];
}else{
_feedItemList.addAll(fl);
}
notifyListeners();
}
///methods
Future<void> fetchFeedTypes() async {
feedTypeLoaderStatus = true;
error = '';
try {
var data = await _feedService.fetchFeedTypes(); //...http call
List<FeedType> rawFeedTypes = [];
for (int i = 0; i < data.length; i++) {
rawFeedTypes.add(FeedType.fromJson(data[i]));
}
feedTypeList = rawFeedTypes;
} catch (err) {
//...error handling codes
} finally {
feedTypeLoaderStatus = false;
}
}
Future<void> fetchFeedList({#required int page, String feedId}) async {
if(page == 1){
feedList = [];
}
try {
Map<String, dynamic> queryParams = {"page": page.toString()};
if(feedId != null){
queryParams["feed_type_id"] = feedId;
}
var data = await _feedService.fetchFeedList(queryParams); //...http calls
List<FeedItem> rawFeedList = [];
for (int i = 0; i < data.length; i++) {
rawFeedList.add(FeedItem.fromJson(data[i]));
}
feedList = rawFeedList;
} catch (err) {
feedList = [];
//.... error handling codes
} finally {
feedLoaderStatus = false;
}
}
}
Screenshot
Cant figure out the mistake here. Any suggestion/code improvements are welcome
i trying to send data to firebase i did every thing
this my code
my code
the modal
import 'package:firebase_database/firebase_database.dart';
class Student {
String _id;
String _name;
String _age;
String _city;
String _department;
String _description;
Student(this._id, this._name, this._age, this._city, this._department, this._description);
Student.map(dynamic obj) {
this._id = obj['id'];
this._name = obj['name'];
this._age = obj['age'];
this._city = obj['city'];
this._department = obj['department'];
this._description = obj['_description'];
}
String get id => _id;
String get name => _name;
String get age => _age;
String get city => _city;
String get department => _department;
String get description => _description;
Student.fromSnapShot(DataSnapshot snapshot){
_id = snapshot.value['id'];
_name = snapshot.value['name'];
_age = snapshot.value['age'];
_city = snapshot.value['city'];
_department = snapshot.value['department'];
_description = snapshot.value['_description'];
}
}
and
final studentRef = FirebaseDatabase.instance.reference().child('student');
....
class ListViewStudentState extends State<ListViewStudent> {
List<Student> _students;
StreamSubscription<Event> _onStudentAddedSub;
}
StreamSubscription<Event> _onStudentChangedSub;
#override
void initState() {
_students = List();
_onStudentAddedSub = studentRef.onChildAdded.listen(_onStudentAdded);
_onStudentChangedSub = studentRef.onChildChanged.listen(_onStudentChanged);
super.initState();
}
#override
void dispose() {
// TODO: implement dispose
super.dispose();
_onStudentAddedSub.cancel();
_onStudentChangedSub.cancel();
}
...
void _onStudentAdded(Event event) {
setState(() {
_students.add(Student.fromSnapShot(event.snapshot));
});
}
void createNewStudent(BuildContext context) async{
await Navigator.push(context, MaterialPageRoute(builder: (context)=> StudentScreen(Student('', '', '', '', '', ''))));
}
....
}
and the StudentScreen widget code:
class StudentScreen extends StatefulWidget {
final Student student;
StudentScreen(this.student);
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return StudenScreenState();
}
}
class StudenScreenState extends State<StudentScreen> {
TextEditingController _nameController;
TextEditingController _ageController;
TextEditingController _cityController;
TextEditingController _deptController;
TextEditingController _noteController;
#override
void initState() {
_nameController = TextEditingController(text: widget.student.name);
_ageController = TextEditingController(text: widget.student.age);
_cityController = TextEditingController(text: widget.student.city);
_deptController = TextEditingController(text: widget.student.department);
_noteController = TextEditingController(text: widget.student.description);
super.initState();
}
....
FlatButton(
onPressed:(){
if(widget.student.id != null){
studentRef.child(widget.student.id).set({
'name': _nameController.text,
'age': _ageController.text,
'city': _cityController.text,
'department': _deptController.text,
'_description': _noteController.text,
}).then((_){
Navigator.pop(context);
});
}else {
studentRef.push().set({
'name': _nameController.text,
'age': _ageController.text,
'city': _cityController.text,
'department': _deptController.text,
'_description': _noteController.text,
}).then((_){
Navigator.pop(context);
});
}
},
child: (widget.student.id != null)? Text('Update'): Text('Add'),
...
}
it's gives me this error when try to push the data
E/flutter (15862): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: Bad state: No element
E/flutter (15862): #0 ListMixin.singleWhere (dart:collection/list.dart:185:5)
E/flutter (15862): #1 ListViewStudentState._onStudentChanged (package:flutter_app/ui/listview_student.dart:82:37)
It's just app for test and learning about student
there the StudentList widget for show list of the student and StudentScreen for Add or Edit student
so it must doing push if i pressed on this Flatbutton but it's gives the error above when i do it,
i don't know what i must to do
i searched for long time about the problem and i didn't found anything
and it's gives this error too when run the app
[ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: type 'String' is not a subtype of type 'int' of 'index'
E/flutter (15862): #0 new Student.fromSnapShot (package:flutter_app/_modal/student.dart:28:25)
E/flutter (15862): #1 ListViewStudentState._onStudentAdded. (package:flutter_app/ui/listview_student.dart:78:29)
E/flutter (15862): #2 State.setState (package:flutter/src/widgets/framework.dart:1148:30)
E/flutter (15862): #3 ListViewStudentState._onStudentAdded (package:flutter_app/ui/listview_student.dart:77:5)
can help please!
StateError of Bad State: No Element is thrown whenever Dart tries to access an element inside a List object which is empty.
Example:
List<Foo> foos = [];
foos.first;
=> Bad state: No element
Do the above and you'll get a "Bad State: No element" exception thrown.
To avoid this you have to check whether your list is empty before trying to access any element.
if (foos != null && foos.isNotEmpty)
foos.first;
If you're using a List method such as .firstWhere or .singleWhere you can specify an orElse clause which will be used when there are no elements or no elements found.
foos.firstWhere((e) => e.id == 1, orElse: () => null);
The above will return null whenever there are no elements or when no element matches the "where" criteria.
Null Safe
With the release of Null safe Dart in Flutter 2, a version of .firstWhere called .firstWhereOrNull which can return either the Object type of your List (if found), or null, is available in an Iterable extension class inside package:collection/collection.dart.
So the above would become:
import 'package:collection/collection.dart'; // don't forget this at top of file
Foo? firstFoo = foos.firstWhereOrNull((e) => e.id == 1);
Where, if a list item has id == 1 it will be returned, else, null is returned.