Related
Hell everyone so im building an app and i want to implement firebase messaging, but i cant get it to work. Everything works up to great till this line of code
void getToken() async {
String? token = await FirebaseMessaging.instance.getToken();
print("this is the token" + token!);
}
the app asks for permission to send messages but when it tries to get the token for the device it throws this error.
E/flutter ( 5116): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: [firebase_messaging/unknown] java.io.IOException: java.util.concurrent.ExecutionException: java.io.IOException: AUTHENTICATION_FAILED
E/flutter ( 5116): #0 StandardMethodCodec.decodeEnvelope (package:flutter/src/services/message_codecs.dart:653:7)
E/flutter ( 5116): #1 MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:296:18)
E/flutter ( 5116): <asynchronous suspension>
E/flutter ( 5116): #2 MethodChannel.invokeMapMethod (package:flutter/src/services/platform_channel.dart:499:43)
E/flutter ( 5116): <asynchronous suspension>
E/flutter ( 5116): #3 MethodChannelFirebaseMessaging.getToken (package:firebase_messaging_platform_interface/src/method_channel/method_channel_messaging.dart:224:11)
E/flutter ( 5116): <asynchronous suspension>
E/flutter ( 5116): #4 _LobbyState.getToken (package:myfuji/screens/Lobby.dart:61:21)
E/flutter ( 5116): <asynchronous suspension>
E/flutter ( 5116):
W/DynamiteModule( 5116): Local module descriptor class for com.google.android.gms.providerinstaller.dynamite not found.
I/DynamiteModule( 5116): Considering local module com.google.android.gms.providerinstaller.dynamite:0 and remote module com.google.android.gms.providerinstaller.dynamite:0
W/ProviderInstaller( 5116): Failed to load providerinstaller module: No acceptable module com.google.android.gms.providerinstaller.dynamite found. Local version is 0 and remote version is 0.
D/EGL_emulation( 5116): app_time_stats: avg=84.66ms min=17.81ms max=417.64ms count=11
Can anyone please point me to the right direction? Much appreciated. I have also included the complete code for that section
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import 'package:myfuji/fetchInfo/userCart.dart';
import 'package:myfuji/screens/Chat.dart';
import 'package:myfuji/screens/News.dart';
import 'package:myfuji/screens/mindYoBis.dart';
import 'package:myfuji/screens/Shop.dart';
import '../fetchInfo/my_Drawer.dart';
import 'SignIn.dart';
class Lobby extends StatefulWidget {
const Lobby({Key? key}) : super(key: key);
#override
State<Lobby> createState() => _LobbyState();
}
class _LobbyState extends State<Lobby> {
String? mToken = " ";
FirebaseMessaging messaging =
FirebaseMessaging.instance; //getting firebase messaging ready for use
List<String> docIDs = [];
Future getDocId() async {
await FirebaseFirestore.instance.collection('user').get().then(
(snapshot) => snapshot.docs.forEach(
(document) {
print(document.reference);
docIDs.add(document.reference.id);
},
),
);
}
late final CollectionReference usersCollection = FirebaseFirestore.instance
.collection('user'); //gets 'user' collection from firebase
final FirebaseAuth auth = FirebaseAuth.instance;
int index = 0;
final screen = [
const News(),
const mindYoBis(),
const Shop(),
const Chat()
]; // lower drawer
#override
void initState() {
super.initState();
requestPermission();
getToken();
}
// get device token and print
void getToken() async {
String? token = await FirebaseMessaging.instance.getToken();
print("this is the token" + token!);
}
// end
// request permission to get message from app
void requestPermission() async {
NotificationSettings settings = await messaging.requestPermission(
alert: true,
announcement: false,
badge: true,
carPlay: false,
criticalAlert: false,
provisional: false,
sound: true,
);
if (settings.authorizationStatus == AuthorizationStatus.authorized) {
print('User granted permission');
} else if (settings.authorizationStatus ==
AuthorizationStatus.provisional) {
print('User granted provisional permission');
} else {
print('User declined or has not accepted permission');
}
}
// end of permission
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData.dark(),
home: Scaffold(
endDrawer: my_Drawer(documentId: auth.currentUser!.uid),
appBar: AppBar(
// cart bottom/icon
actions: [
IconButton(
padding: EdgeInsets.only(right: 10.0),
icon: const Icon(
Icons.shopping_cart,
),
onPressed: () => {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) =>
const userCart())) //takes user to cart
},
),
Builder(builder: (context) {
return IconButton(
padding: const EdgeInsets.only(right: 10.0),
icon: const Icon(
Icons.menu,
),
onPressed: () => {Scaffold.of(context).openEndDrawer()},
);
}),
],
title: const Text('Welcome'),
leading: Image.asset('src/logo.png'),
backgroundColor: Colors.deepPurple,
),
//body
body: screen[index], // screen navi
//body
bottomNavigationBar: NavigationBarTheme(
data: NavigationBarThemeData(
backgroundColor: Colors.deepPurple,
indicatorColor: Colors.deepPurple[100],
labelTextStyle: MaterialStateProperty.all(
const TextStyle(fontSize: 18, fontWeight: FontWeight.w500))),
//lower bottom navigation
child: NavigationBar(
labelBehavior: NavigationDestinationLabelBehavior.onlyShowSelected,
selectedIndex: index,
onDestinationSelected: (index) =>
setState(() => this.index = index),
destinations: const [
NavigationDestination(icon: Icon(Icons.home), label: 'Home'),
NavigationDestination(
icon: Icon(Icons.emoji_food_beverage_rounded),
label: 'Local Bis'),
NavigationDestination(
icon: Icon(Icons.shopping_bag), label: 'Shop'),
NavigationDestination(icon: Icon(Icons.chair), label: 'Games')
],
),
),
),
);
}
//sign out
signOut() async {
await auth.signOut();
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (context) => const SignIn()));
}
}
```code
I added multiple blocs in my main file and use 1 bloc for every single screen, but when i go to next screen and come back to previous screen then again try to trigger event it gives error of bad state of second bloc.
main class
void main() {
runApp(MultiBlocProvider(
providers:[
BlocProvider<LoginBloc>(
create:(_) => LoginBloc(),
),
BlocProvider<HomeBloc>(
create: (_) => HomeBloc()
),
BlocProvider<UnitBloc>(
create: (_) => UnitBloc()
),
],
child: MyApp()
));
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Watchman',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: MyColors.primaryTheme,
bottomSheetTheme: BottomSheetThemeData(backgroundColor: Colors.transparent)
// scaffoldBackgroundColor: MyColors.colorBGBrown
),
home: LoginPage(),
);
}
}
login screen
class LoginPage extends StatefulWidget {
LoginPage({Key? key}) : super(key: key);
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
final userController = TextEditingController();
final pswdController = TextEditingController();
String userName = '';
String password = '';
#override
void dispose() {
// TODO: implement dispose
userController.dispose();
pswdController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => LoginBloc(),
child: BlocListener<LoginBloc, LoginState>(
listener: (context, state) {
if (state is LoginSuccessState) {
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => HomePage()),
);
} else if (state is LoginErrorState) {
showInSnackBar(context, state.error);
}
},
child: Scaffold(
appBar: AppBar(
title: Text('Login'),
),
body: BlocBuilder<LoginBloc, LoginState>(
builder: (context, state) {
if (state is LoginLoadingState) {
return const Center(
child: CircularProgressIndicator()
);
} else {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Image.asset(
'Assets/iclogo.png',
),
SizedBox(height: 40),
Container(
padding: EdgeInsets.symmetric(
vertical: 5, horizontal: 10),
child: TextFormFieldWidget(
labelText: 'UserName',
controller: userController,
),
),
Container(
padding: EdgeInsets.symmetric(
vertical: 5, horizontal: 10),
child: TextFormFieldWidget(
labelText: 'Password',
isPassword: true,
controller: pswdController,
),
),
Container(
width: double.infinity,
padding: EdgeInsets.symmetric(
vertical: 5, horizontal: 10),
child: ElevatedButtonWidget(
onPressed: () {
userName = userController.text;
password = pswdController.text;
context.read<LoginBloc>().add(
LoginEventSubmit(
username: userName,
password: password)
);
},
text: 'Login',
bgColor: MyColors.colorPrimary,
textColor: Colors.white,
),
),
],
);
}
},
),
),
),
);
}
void showInSnackBar(BuildContext context, String value) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(value),
// duration: Duration(seconds: 2),
),
);
}
}
login_bloc
class LoginBloc extends Bloc<LoginEvent, LoginState> {
LoginBloc() : super(LoginInitial()) {
on<LoginEvent>((event, emit) async {
// TODO: implement event handler
if(event is LoginEventSubmit){
if(event.username.isEmpty || event.password.isEmpty){
emit(LoginErrorState("Please Enter all Fields"));
}else{
emit(LoginLoadingState());
try{
print(event.username +".."+ event.password);
final LoginModel user = await login(event.username ,event.password);
if(user.result=="200"){
emit(LoginSuccessState(user));
}else {
emit(LoginErrorState("UserName or Password is wrong"));
}
}catch (e){
print("error..."+e.toString());
emit(LoginErrorState("Login Failed"));
}
}
}
});
}
Future<LoginModel> login(String username, String pwd) async {
final Dio _dio = Dio();
final queryParameters = {
'username': username,
'pswd':pwd,
};
final response = await _dio.post(AppConstants.login,queryParameters: queryParameters);
final jsonResponse =response.data;
print(jsonResponse.toString());
return LoginModel.fromJson(jsonResponse);
}
}
home screen
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
// Initial Selected Value
#override
void initState() {
// TODO: implement initState
super.initState();
}
Development _selected = Development(
dEVNAME: "Select Building",
dEVCODE: "0"
);
// List of items in our dropdown menu
List<Development>? devList ;
#override
Widget build(BuildContext context) {
return BlocProvider<HomeBloc>(
create: (context) => BlocProvider.of<HomeBloc>(context)..add(GetDevEvent()),
child: BlocListener<HomeBloc, HomeState>(
listener: (context, state) {
// TODO: implement listener
if(state is HomeInitial){
}else if (state is HomeErrorState){
showInSnackBar(context, state.error);
}else if(state is HomeLoadedState){
// devList?.addAll(state.devList);
devList= state.devList;
devList?.add(_selected);
}else if(state is HomeSelectedState){
Navigator.push(
context,
MaterialPageRoute(builder: (context) => UnitsPage(
devCode: state.devCode,
devName: state.devName,
)
)
);
}
},
child: Scaffold(
appBar: AppBar(
title: const Text("Home"),
),
body: BlocBuilder<HomeBloc, HomeState>(
builder: (context, state) {
if (state is HomeLoadingState) {
return const Center(
child: CircularProgressIndicator()
);
} else {
return Center(
child: Padding(
padding:
const EdgeInsets.symmetric(vertical: 10, horizontal: 15),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
padding: EdgeInsets.all(5),
decoration: BoxDecoration(
border: Border.all(color: MyColors.colorBlack),
borderRadius: BorderRadius.circular(15),
),
child: DropdownButton<Development>(
// Initial Value
value: _selected,
isExpanded: true,
// Down Arrow Icon
icon: const Icon(Icons.keyboard_arrow_down),
iconSize: 25,
// Array list of items
items: devList?.map<DropdownMenuItem<Development>>((Development value) {
return DropdownMenuItem<Development>(
value: value,
child: Text(value.dEVNAME!),
);
}).toList(),
// After selecting the desired option,it will
// change button value to selected value
onChanged: (newVal) => setState(() => _selected = newVal!),
)
),
SizedBox(
height: 20,
),
Container(
width: double.infinity,
padding:
EdgeInsets.symmetric(horizontal: 20, vertical: 10),
child: ElevatedButtonWidget(
onPressed: () {
if(_selected.dEVCODE!="0"){
BlocProvider.of<HomeBloc>(context).add(
DevSelectEvent(
devCode:_selected.dEVCODE,
devName: _selected.dEVNAME,
)
);
}else{
String msg = "please "+_selected.dEVNAME!;
showInSnackBar(context, msg);
}
},
text: 'Search',
),
)
],
),
),
);
}
},
),
),
),
);
}
void showInSnackBar(BuildContext context, String value) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(value),
// duration: Duration(seconds: 2),
),
);
}
}
home bloc
class HomeBloc extends Bloc<HomeEvent, HomeState> {
HomeBloc() : super(HomeInitial()) {
on<HomeEvent>((event, emit) async {
// TODO: implement event handler
if(event is GetDevEvent){
emit(HomeLoadingState());
try{
DevData devList = await getData();
if(devList.result =="1"){
emit(HomeLoadedState(devList.development));
print("data.."+ devList.toString());
}else{
emit(HomeErrorState("No data Found"));
}
}catch (e){
print("error..."+e.toString());
emit(HomeErrorState("Failed"));
}
}
if(event is DevSelectEvent){
emit(HomeLoadingState());
emit(HomeSelectedState(event.devName!,event.devCode!));
}
});
}
Future<DevData> getData() async {
final Dio _dio = Dio();
final response = await _dio.post(AppConstants.getDevs);
final jsonResponse =response.data;
print(jsonResponse.toString());
return DevData.fromJson(jsonResponse);
}
}
Error log:
Bad state: Tried to read a provider that threw during the creation of its value.
The exception occurred during the creation of type HomeBloc.
══╡ EXCEPTION CAUGHT BY PROVIDER ╞═══════════════════════════════
The following StateError was thrown:
Bad state: Cannot add new events after calling close
When the exception was thrown, this was the stack:
#1 Bloc.add (package:bloc/src/bloc.dart:84:24)
#2 _HomePageState.build.<anonymous closure> (package:watchman/Home/HomePage.dart:39:64)
#3 _CreateInheritedProviderState.value (package:provider/src/inherited_provider.dart:729:36)
#4 _InheritedProviderScopeElement.value (package:provider/src/inherited_provider.dart:583:33)
#5 Provider.of (package:provider/src/provider.dart:303:37)
#6 ReadContext.read (package:provider/src/provider.dart:656:21)
#7 _BlocListenerBaseState.initState (package:flutter_bloc/src/bloc_listener.dart:147:36)
#8 StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:4942:57)
#9 ComponentElement.mount (package:flutter/src/widgets/framework.dart:4781:5)
#10 SingleChildWidgetElementMixin.mount (package:nested/nested.dart:222:11)
#11 Element.inflateWidget (package:flutter/src/widgets/framework.dart:3817:16)
#12 Element.updateChild (package:flutter/src/widgets/framework.dart:3551:18)
#13 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4832:16)
#14 _InheritedProviderScopeElement.performRebuild (package:provider/src/inherited_provider.dart:495:11)
#15 Element.rebuild (package:flutter/src/widgets/framework.dart:4529:5)
#16 ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:4787:5)
#17 ComponentElement.mount (package:flutter/src/widgets/framework.dart:4781:5)
#18 _InheritedProviderScopeElement.mount (package:provider/src/inherited_provider.dart:395:11)
... Normal element mounting (7 frames)
#25 SingleChildWidgetElementMixin.mount (package:nested/nested.dart:222:11)
... Normal element mounting (7 frames)
#32 SingleChildWidgetElementMixin.mount (package:nested/nested.dart:222:11)
... Normal element mounting (283 frames)
#315 Element.inflateWidget (package:flutter/src/widgets/framework.dart:3817:16)
#316 MultiChildRenderObjectElement.inflateWidget (package:flutter/src/widgets/framework.dart:6350:36)
#317 Element.updateChild (package:flutter/src/widgets/framework.dart:3551:18)
#318 RenderObjectElement.updateChildren (package:flutter/src/widgets/framework.dart:5883:32)
#319 MultiChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6375:17)
#320 Element.updateChild (package:flutter/src/widgets/framework.dart:3530:15)
#321 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4832:16)
#322 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4977:11)
I solve this issue by adding my first event inside initstate(), instead of BlocProvder.
late HomeBloc bloc;
// Initial Selected Value
#override
void initState() {
// TODO: implement initState
bloc = HomeBloc();
bloc.add(GetDevEvent());
super.initState();
}
#override
void dispose() {
// TODO: implement dispose
bloc.close();
super.dispose();
}
I am getting this error when trying to select a different value in DropDownButton. But not able to find the error.
Setting up the BLoC pattern is even easier with the dependency injection features of the Provider package and Streams from RXDart. Shared Preferences makes it possible to store and restore data to and from the bloc when state is lost.
In this video we'll demonstrate the usefulness of all three packages by creating a dynamic theme through BLoC that is seamlessly saved to and restored from the device on startup.
I have followed this example https://www.youtube.com/watch?v=ZpLQIFUqaGI
Main Class
void main() => runApp(Start ());
class Start extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
]);
final String appTitle = "Opn same";
pageTitle(appTitle);
return ChangeNotifierProvider(
create: (BuildContext context) => PreferenceProvider(),
child: Consumer<PreferenceProvider>(
builder: (context, provider, child) {
return StreamBuilder<ThemeModel>(
stream: provider.bloc.customTheme,
builder: (context, snapshotCustomTheme) {
if (!snapshotCustomTheme.hasData) return Container();
return MaterialApp(
theme: snapshotCustomTheme.data!.customTheme,
title: appTitle,
initialRoute: "/Setting",
routes: routes,
);
});
},
),
);
}
}
Setting class:
class Setting extends StatefulWidget {
final String title;
Settings(this.title);
#override
_SettingsState createState() => _SettingsState();
}
class _SettingsState extends State<Settings> {
#override
Widget build(BuildContext context) {
final bloc = Provider.of<Preference>(context).bloc;
return Scaffold(
appBar: AppBar(
title: Text("Settings"),
leading: GestureDetector(
child: Icon(CupertinoIcons.back),
onTap: () {
bloc.savePreferences();
Navigator.of(context).pop();
},
)),
body: Center(
child: ListView(
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(horizontal: 15.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text("Theme"),
StreamBuilder<ThemeModel>(
stream: bloc.customTheme,
builder: (context, snapshot) {
if (!snapshot.hasData) return Container();
return Container(
padding: EdgeInsets.all(20),
child: DropdownButton(
value: snapshot.data!.value,
items: [
DropdownMenuItem(
child: Text("Default"),
value: 1,
),
DropdownMenuItem(
child: Text("Drala"),
value: 2,
),
DropdownMenuItem(
child: Text("gold"),
value: 3,
)
],
onChanged: (int? value) {
print("value = $value");
setState(() {
bloc.changeTheme(bloc.inToTheme(value!)); **// Getting error here**
});
},
),
);
})
],
),
),
],
),
),
);
}
}
ThemeModel Class
class ThemeModel{
final ThemeData customTheme;
final int value;
ThemeModel ({ required this.customTheme, required this.value,});
}
preference bloc:
class PreferenceBloc {
final _customTheme = BehaviorSubject<ThemeModel>();
//Getters
Stream<Theodel> get customTheme => _customTheme.stream;
//Setters
Function(ThemeModel) get chanheme => _customTheme.sink.add;
indexToTheme(int value) {
return _customTheme.firstWhere((x) => x.value==value);
}
savePreferences() async {
SharedPreferences prefs = await Shareferences.getInstance();
await prefs.setInt('themeValue', _customTheme.value.value);
}
loadPreferences() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
int? themeValue = prefs.getInt('themeValue');
if (themeValue != null){
changeTheme(indeheme(themeValue));
} else {
changeTheme(ThemeModel(customTheme: ThemeData(
primaryColor: Color.fromRGBO(58, 66, 86, 1.0),
scaffoldBackgroundColor: Color.fromRGBO(247, 247, 247, 1.0),
brightness: Brightness.light,
visualDensity: VisualDensity(vertical: 0.5, horizontal: 0.5),
primaryColorBrightness: Brightness.light,
primaryTextTheme: TextTheme(
headline6: TextStyle(
fontWeight: FontWeight.w600,
fontFamily: "Raleway",
fontStyle: FontStyle.normal,
fontSize: 22.0,
letterSpacing: 1.6,
wordSpacing: 1.6,
color: Colors.white70,
),
),
),value: 2, ));
}
}
dispose() {
_customTheme.close();
}
}
class PreferenceProvider with ChangeNotifier {
late ThemePreferenceBloc _bloc;
ThemePreferenceProvider() {
_bloc = ThemePreferenceBloc();
_bloc.loadPreferences();
}
ThemeBloc get bloc => _bloc;
}
Error I am getting:
Performing hot restart...
Syncing files to device Android SDK built for x86...
Restarted application in 1,900ms.
I/zygote ( 6068): Do full code cache collection, code=120KB, data=83KB
I/zygote ( 6068): After code cache collection, code=111KB, data=66KB
I/flutter ( 6068): value = 3
E/flutter ( 6068): [ERROR:flutter/lib/ui/ui_dart_state.cc(199)] Unhandled Exception: type 'Future<ThemeModel>' is not a subtype of type 'ThemeModel'
E/flutter ( 6068): #0 _SettingsState.build.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:open_sesame/view/settings/settings.dart:65:55)
E/flutter ( 6068): #1 State.setState (package:flutter/src/widgets/framework.dart:1088:30)
E/flutter ( 6068): #2 _SettingsState.build.<anonymous closure>.<anonymous closure> (package:open_sesame/view/settings/settings.dart:64:31)
E/flutter ( 6068): #3 _DropdownButtonState._handleTap.<anonymous closure> (package:flutter/src/material/dropdown.dart:1266:25)
E/flutter ( 6068): #4 _rootRunUnary (dart:async/zone.dart:1362:47)
E/flutter ( 6068): #5 _CustomZone.runUnary (dart:async/zone.dart:1265:19)
E/flutter ( 6068): <asynchronous suspension>
E/flutter ( 6068):
Please help.. Thanks in advance..
If i have to guess you're missing an await where you're calling the function of type Future<ThemeModel>
Maybe share a full example if you want to debug it here further.
The reason I was not able to get it working was I was not able to access the data stored in another file. So now I brought the data into the same file.
import 'package:flutter/material.dart';
import 'package:rxdart/subjects.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:testing/models/color.dart';
class PreferenceBloc{
final _brightness = BehaviorSubject<Brightness>();
final _primaryColor = BehaviorSubject<ColorModel>();
final _colors = [
ColorModel(color: Colors.blue,index: 0.0, name: 'Blue'),
ColorModel(color: Colors.green,index: 1.0, name: 'Green'),
ColorModel(color: Colors.red,index: 2.0, name: 'Red'),
ColorModel(color: Colors.white,index: 3.0, name: 'White'),
];
//Getters
Stream<Brightness> get brightness => _brightness.stream;
Stream<ColorModel> get primaryColor => _primaryColor.stream;
//Setters
Function(Brightness) get changeBrightness => _brightness.sink.add;
Function(ColorModel) get changePrimaryColor => _primaryColor.sink.add;
indexToPrimaryColor(double index){
return _colors.firstWhere((x) => x.index==index);
}
savePreferences() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
if (_brightness.value == Brightness.light){
await prefs.setBool('dark', false);
} else {
await prefs.setBool('dark', true);
}
await prefs.setDouble('colorIndex', _primaryColor.value.index);
}
loadPreferences() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
bool? darkMode=prefs.getBool('dark');
double? colorIndex = prefs.getDouble('colorIndex');
if (darkMode != null){
(darkMode == false) ? changeBrightness(Brightness.light) : changeBrightness(Brightness.dark);
} else {
changeBrightness(Brightness.light);
}
if (colorIndex != null){
changePrimaryColor(indexToPrimaryColor(colorIndex));
} else {
changePrimaryColor(ColorModel(color: Colors.blue,index: 0.0, name: 'Blue'));
}
}
dispose(){
_primaryColor.close();
_brightness.close();
}
}
So, I was trying to write a webRTC mobile app. Caught some error.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart';
import 'package:sdp_transform/sdp_transform.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'WebRTC lets learn together'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
CollectionReference firebaseInstance =
FirebaseFirestore.instance.collection("dmeet");
RTCPeerConnection _peerConnection;
MediaStream _localStream;
RTCVideoRenderer _remoteRenderer = RTCVideoRenderer();
var docId = TextEditingController();
var l;
_createOfferSdp() async {
RTCSessionDescription description =
await _peerConnection.createOffer({'offerToReceiveAudio': 1});
var session = parse(description.sdp);
Map<String, dynamic> sdp = {
"sdp": session.toString(),
};
var k = firebaseInstance.doc().collection("sdp").doc("offersdp").set(sdp);
print("K val: $k");
_peerConnection.setLocalDescription(description);
// print(session);
}
bool remotesaved = false;
_createAnswerSdp() async {
firebaseInstance
.doc(docId.text)
.collection("sdp")
.doc("offersdp")
.get()
.then((value) async {
print("here");
var temp = (value.data()["sdp"]).toString();
// var session = await jsonDecode(temp.toString());
// print("here3");
// print("session: $session");
// String sdp = write(session, null);
print(temp);
if (temp != null) {
RTCSessionDescription description1 =
RTCSessionDescription(temp, "offer");
await _peerConnection.setRemoteDescription(description1).then((value) {
setState(() {
remotesaved = true;
});
});
}
});
if (remotesaved) {
RTCSessionDescription description = await _peerConnection
.createAnswer({'offerToReceiveVideo': 0, 'offerToReceiveAudio': 1});
var session = parse(description.sdp);
firebaseInstance
.doc(docId.text)
.collection("sdp")
.doc("answersdp")
.set(session);
if (l != null) {
firebaseInstance
.doc(docId.text)
.collection("icecandidate")
.doc("answerice")
.set(l);
}
}
}
showAlertDialog(BuildContext context) {
// set up the buttons
Widget cancelButton = FlatButton(
child: Text("Cancel"),
onPressed: () {},
);
Widget continueButton = FlatButton(
child: Text("Continue"),
onPressed: _createAnswerSdp,
);
// set up the AlertDialog
AlertDialog alert = AlertDialog(
title: Text("AlertDialog"),
content: TextField(
controller: docId,
),
actions: [
cancelButton,
continueButton,
],
);
// show the dialog
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
}
#override
void initState() {
_createPeerConnection().then((pc) {
_peerConnection = pc;
});
super.initState();
}
#override
void dispose() {
super.dispose();
}
_getUserMedia() async {
final Map<String, dynamic> mediaConstraints = {
'audio': true,
'video': false,
};
MediaStream stream = await navigator.getUserMedia(mediaConstraints);
// _localStream = stream;
// _peerConnection.addStream(stream);
return stream;
}
_createPeerConnection() async {
Map<String, dynamic> configuration = {
"iceServers": [
{"url": "stun:stun.l.google.com:19302"},
]
};
final Map<String, dynamic> offerSdpConstraints = {
"mandatory": {
"OfferToReceiveAudio": true,
"OfferToReceiveVideo": false,
},
"optional": [],
};
_localStream = await _getUserMedia();
RTCPeerConnection pc =
await createPeerConnection(configuration, offerSdpConstraints);
// if (pc != null) print(pc);
pc.addStream(_localStream);
pc.onIceCandidate = (e) {
if (e.candidate != null) {
l = json.encode({
'candidate': e.candidate.toString(),
'sdpMid': e.sdpMid.toString(),
'sdpMlineIndex': e.sdpMlineIndex,
});
print(l);
}
};
pc.onIceConnectionState = (e) {
print(e);
};
pc.onAddStream = (stream) {
print('addStream: ' + stream.id);
_remoteRenderer.srcObject = stream;
};
return pc;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Center(
child: Row(
children: [
ElevatedButton(
child: Text("Create"),
onPressed: _createOfferSdp,
),
ElevatedButton(
onPressed: () {
showAlertDialog(context);
},
child: Text("Join"),
)
],
),
),
),
);
}
}
So, In my code from a mobile an offer will be created and stored in firestore.
dmeet->(AUTO ID)->sdp->offersdp->sdp:sdpvaluesStored
so after that I given the documentId manually in join button and when I try to fetch the offersdp and to set the remote description. I came up with some error
D/FlutterWebRTCPlugin( 7232): peerConnectionSetRemoteDescription(): WEBRTC_SET_REMOTE_DESCRIPTION_ERROR: SessionDescription is NULL.
E/flutter ( 7232): [ERROR:flutter/lib/ui/ui_dart_state.cc(186)] Unhandled Exception: Unable to RTCPeerConnection::setRemoteDescription: peerConnectionSetRemoteDescription(): WEBRTC_SET_REMOTE_DESCRIPTION_ERROR: SessionDescription is NULL.
E/flutter ( 7232): #0 RTCPeerConnectionNative.setRemoteDescription
package:flutter_webrtc/…/native/rtc_peerconnection_impl.dart:328
E/flutter ( 7232): <asynchronous suspension>
E/flutter ( 7232): #1 _MyHomePageState._createAnswerSdp.<anonymous closure>.<anonymous closure> (package:flutter_voip_calling/main.dart)
I'm using the tutorial code where it uses the same concept as that I will paste the sdp in textbox and set the remote description and the tutorial code is here
I have a single screen that uses the connectivity package from the Flutter dev team. I am able to build golden images for this widget when I don't use the connectivity package, but when I add it the way that is outlined by the Flutter dev team on their page on pub.dev, I encounter exceptions when running the tests via the flutter test --update-goldens command.
I have included the test file (test/widget/widget_test.dart), main.dart, welcome_screen.dart, and the output from running the tests. I tried looking for similar issues that others have experienced online, but my efforts were not fruitful; I am looking for help in resolving this issue. Any advice or suggestions would be greatly appreciated! 🙏
Output
from flutter test --update-goldens
══╡ EXCEPTION CAUGHT BY SERVICES LIBRARY ╞══════════════════════════════════════════════════════════
The following MissingPluginException was thrown while activating platform stream on channel
plugins.flutter.io/connectivity_status:
MissingPluginException(No implementation found for method listen on channel
plugins.flutter.io/connectivity_status)
When the exception was thrown, this was the stack:
#0 MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:156:7)
<asynchronous suspension>
<asynchronous suspension>
(elided one frame from package:stack_trace)
...
════════════════════════════════════════════════════════════════════════════════════════════════════
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following MissingPluginException was thrown running a test:
MissingPluginException(No implementation found for method check on channel
plugins.flutter.io/connectivity)
When the exception was thrown, this was the stack:
#0 MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:156:7)
<asynchronous suspension>
<asynchronous suspension>
(elided one frame from package:stack_trace)
...
The test description was:
WelcomeScreen Golden test
════════════════════════════════════════════════════════════════════════════════════════════════════
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following message was thrown:
Multiple exceptions (2) were detected during the running of the current test, and at least one was
unexpected.
════════════════════════════════════════════════════════════════════════════════════════════════════
00:04 +14 -1: /Users/---/Documents/---/---/---/test/widget/widget_test.dart: WelcomeScreen Golden test [E]
Test failed. See exception logs above.
The test description was: WelcomeScreen Golden test
00:04 +14 -1: Some tests failed.
Test: widget_test.dart
void main() {
testWidgets('WelcomeScreen Golden test', (WidgetTester tester) async {
await tester.pumpWidget(MyApp());
await expectLater(
find.byType(MyApp),
matchesGoldenFile('main.png'),
);
});
}
main.dart
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
FocusManager.instance.primaryFocus.unfocus();
},
child: MaterialApp(
initialRoute: WelcomeScreen.id,
routes: {
WelcomeScreen.id: (context) => WelcomeScreen(),
DashboardScreen.id: (context) => DashboardScreen(),
},
),
);
}
}
Screen: welcome_screen.dart
class WelcomeScreen extends StatefulWidget {
static const String id = 'welcome_screen';
#override
_WelcomeScreenState createState() => _WelcomeScreenState();
}
class _WelcomeScreenState extends State<WelcomeScreen> {
ConnectivityResult _connectionStatus = ConnectivityResult.none;
final Connectivity _connectivity = Connectivity();
StreamSubscription<ConnectivityResult> _connectivitySubscription;
String username = '';
String password = '';
bool isLoading = false;
#override
void initState() {
super.initState();
_connectivitySubscription = _connectivity.onConnectivityChanged.listen(_updateConnectionStatus);
initConnectivity();
}
#override
void dispose() {
_connectivitySubscription.cancel();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Padding(
padding: EdgeInsets.symmetric(horizontal: 24.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Image.asset('images/logo.png'),
RoundedTextField(
textInputAction: TextInputAction.next,
placeholder: 'Username',
icon: Icons.person,
color: Colors.lightBlueAccent,
onChanged: (value) {
setState(() {
username = value;
});
},
),
RoundedTextField(
textInputAction: TextInputAction.done,
placeholder: 'Password',
icon: Icons.lock,
color: Colors.lightBlueAccent,
password: true,
onChanged: (value) {
setState(() {
password = value;
});
},
),
isLoading
? Center(
child: Padding(
padding: EdgeInsets.symmetric(vertical: 22.0),
child: CircularProgressIndicator(),
),
)
: RoundedButton(
disabled: isLoading,
title: 'Log In',
color: Colors.lightBlueAccent,
onPressed: (_connectionStatus == ConnectivityResult.mobile || _connectionStatus == ConnectivityResult.wifi)
? () async {
setState(() {
isLoading = true;
});
try {
Login login = await API().login(username, password);
if (login.appUserKey != 0) {
Navigator.pushNamed(context, DashboardScreen.id);
}
} catch (e) {
print(e);
}
setState(() {
isLoading = false;
});
}
: null,
),
],
),
),
);
}
// Platform messages are asynchronous, so we initialize in an async method.
Future<void> initConnectivity() async {
ConnectivityResult result;
// Platform messages may fail, so we use a try/catch PlatformException.
try {
result = await _connectivity.checkConnectivity();
} on PlatformException catch (e) {
print(e.toString());
}
// 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 Future.value(null);
}
return _updateConnectionStatus(result);
}
Future<void> _updateConnectionStatus(ConnectivityResult result) async {
switch (result) {
case ConnectivityResult.wifi:
case ConnectivityResult.mobile:
setState(() => _connectionStatus = result);
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Connected to network'),
duration: Duration(seconds: 1),
),
);
break;
case ConnectivityResult.none:
setState(() => _connectionStatus = result);
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Disconnected from network'),
duration: Duration(seconds: 1),
),
);
break;
default:
setState(() => _connectionStatus = null);
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Connectivity failed'),
duration: Duration(seconds: 1),
),
);
break;
}
}
}
I resolved my issue.
The widget was trying to run a method that did not exist for the platform on which the tests were being run. I decided to mock the Connectivity class, send the mocked class to the widget, and then have the widget itself check to see what type of class it had received in order to determine if it should attempt to run the listen method; the method whose invocation had previously been causing exceptions to be thrown. I used the Mockito package to easily mock the service.
I've included the relevant code snippets that resolved the issue for me.
Test: widget_test.dart
Important: Used Mockito to mock the Connectivity class
class MockConnectivity extends Mock implements Connectivity {}
void main() {
testWidgets('WelcomeScreen', (WidgetTester tester) async {
await tester.runAsync(() async {
await tester.pumpWidget(
MaterialApp(
home: WelcomeScreen(
connectivity: MockConnectivity(),
),
),
);
await tester.pumpAndSettle();
});
await expectLater(
find.byType(WelcomeScreen),
matchesGoldenFile('welcome_screen_portrait.png'),
);
});
}
main.dart
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
initialRoute: WelcomeScreen.id,
routes: {
WelcomeScreen.id: (context) => WelcomeScreen(
connectivity: Connectivity(),
),
},
);
}
}
welcome_screen.dart
class WelcomeScreen extends StatefulWidget {
static const String id = 'welcome_screen';
final Connectivity connectivity;
WelcomeScreen({this.connectivity});
#override
_WelcomeScreenState createState() => _WelcomeScreenState();
}
class _WelcomeScreenState extends State<WelcomeScreen> {
Connectivity _connectivity;
ConnectivityResult _connectionStatus = ConnectivityResult.none;
StreamSubscription<ConnectivityResult> _connectivitySubscription;
#override
void initState() {
super.initState();
_connectivity = widget.connectivity;
if (_connectivity.runtimeType == Connectivity) {
_connectivitySubscription = _connectivity.onConnectivityChanged.listen(_updateConnectionStatus);
}
initConnectivity();
}
#override
void dispose() {
controller.dispose();
if (_connectivity.runtimeType == Connectivity) {
_connectivitySubscription.cancel();
}
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Text('Welcome'),
);
}
// Platform messages are asynchronous, so we initialize in an async method.
Future<void> initConnectivity() async {
ConnectivityResult result;
// Platform messages may fail, so we use a try/catch PlatformException.
try {
result = await _connectivity.checkConnectivity();
} on PlatformException catch (e) {
print(e.toString());
}
// 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 Future.value(null);
}
return _updateConnectionStatus(result);
}
Future<void> _updateConnectionStatus(ConnectivityResult result) async {
switch (result) {
case ConnectivityResult.wifi:
case ConnectivityResult.mobile:
setState(() => _connectionStatus = result);
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Connected to network'),
duration: Duration(seconds: 1),
),
);
break;
case ConnectivityResult.none:
setState(() => _connectionStatus = result);
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Disconnected from network'),
duration: Duration(seconds: 1),
),
);
break;
default:
setState(() => _connectionStatus = null);
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Connectivity failed'),
duration: Duration(seconds: 1),
),
);
break;
}
}
}