how to mock firebase_messaging in flutter? - flutter

Hello im trying to mock firebase messaging to get token but when i try to test i get some error,can someone help me to solve this error. This error occur only in testing and not in my emulator or mobile phone. Here is my setupFirebaseAuthMocks. Thank you
my test
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp();
}
void main() {
setupFirebaseAuthMocks();
late ProviderContainer container;
group('AuthenticationControllerTest -', () {
setUpAll(() async {
await Firebase.initializeApp();
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
registerThirdPartyServices();
});
tearDown(() {
unregisterThirdPartyServices();
//container.dispose();
});
});
Error
MissingPluginException(No implementation found for method Messaging#getToken on channel plugins.flutter.io/firebase_messaging)
here is the method im trying to call
Future<Result<Failure, bool>> registerUserFirebaseToken() async {
try {
log.i('Registering Firebase');
final fireBaseMessaging = FirebaseMessaging.instance;
final token = await fireBaseMessaging.getToken();
log.v('Firebase token: $token');
await api.post(
link: '${env.getValue(kAuthUrl)}users/auth/firebase',
body: {'token': token},
hasHeader: true,
);
return const Success(true);
} catch (e) {
return Error(Failure(message: 'Firebase registration went wrong, Please try again!', content: e.toString()));
}
}

For those having the same issue, there is an example of a Mock on the official firebase messaging Github
Depending on your Mockito's version, you may have to update this code a little bit.
Here is the Mock file I'm using with Mockito v5.3.2
// ignore_for_file: require_trailing_commas
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_core_platform_interface/firebase_core_platform_interface.dart';
import 'package:firebase_messaging_platform_interface/firebase_messaging_platform_interface.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
typedef Callback = Function(MethodCall call);
final MockFirebaseMessaging kMockMessagingPlatform = MockFirebaseMessaging();
Future<T> neverEndingFuture<T>() async {
// ignore: literal_only_boolean_expressions
while (true) {
await Future.delayed(const Duration(minutes: 5));
}
}
void setupFirebaseMessagingMocks() {
TestWidgetsFlutterBinding.ensureInitialized();
setupFirebaseCoreMocks();
// Mock Platform Interface Methods
// ignore: invalid_use_of_protected_member
when(kMockMessagingPlatform.delegateFor(app: anyNamed('app')))
.thenReturn(kMockMessagingPlatform);
// ignore: invalid_use_of_protected_member
when(kMockMessagingPlatform.setInitialValues(
isAutoInitEnabled: anyNamed('isAutoInitEnabled'),
)).thenReturn(kMockMessagingPlatform);
}
// Platform Interface Mock Classes
// FirebaseMessagingPlatform Mock
class MockFirebaseMessaging extends Mock
with MockPlatformInterfaceMixin
implements FirebaseMessagingPlatform {
MockFirebaseMessaging() {
TestFirebaseMessagingPlatform();
}
#override
bool get isAutoInitEnabled {
return super.noSuchMethod(Invocation.getter(#isAutoInitEnabled),
returnValue: true, returnValueForMissingStub: true) as bool;
}
#override
FirebaseMessagingPlatform delegateFor({FirebaseApp? app}) {
return super.noSuchMethod(
Invocation.method(#delegateFor, [], {#app: app}),
returnValue: TestFirebaseMessagingPlatform(),
returnValueForMissingStub: TestFirebaseMessagingPlatform(),
) as FirebaseMessagingPlatform;
}
#override
FirebaseMessagingPlatform setInitialValues({bool? isAutoInitEnabled}) {
return super.noSuchMethod(
Invocation.method(
#setInitialValues, [], {#isAutoInitEnabled: isAutoInitEnabled}),
returnValue: TestFirebaseMessagingPlatform(),
returnValueForMissingStub: TestFirebaseMessagingPlatform(),
) as FirebaseMessagingPlatform;
}
#override
Future<RemoteMessage?> getInitialMessage() {
return super.noSuchMethod(Invocation.method(#getInitialMessage, []),
returnValue: neverEndingFuture<RemoteMessage>(),
returnValueForMissingStub: neverEndingFuture<RemoteMessage>())
as Future<RemoteMessage?>;
}
#override
Future<void> deleteToken() {
return super.noSuchMethod(Invocation.method(#deleteToken, []),
returnValue: Future<void>.value(),
returnValueForMissingStub: Future<void>.value()) as Future<void>;
}
#override
Future<String?> getAPNSToken() {
return super.noSuchMethod(Invocation.method(#getAPNSToken, []),
returnValue: Future<String>.value(''),
returnValueForMissingStub: Future<String>.value('')) as Future<String?>;
}
#override
Future<String> getToken({String? vapidKey}) {
return super.noSuchMethod(
Invocation.method(#getToken, [], {#vapidKey: vapidKey}),
returnValue: Future<String>.value(''),
returnValueForMissingStub: Future<String>.value('')) as Future<String>;
}
#override
Future<void> setAutoInitEnabled(bool? enabled) {
return super.noSuchMethod(Invocation.method(#setAutoInitEnabled, [enabled]),
returnValue: Future<void>.value(),
returnValueForMissingStub: Future<void>.value()) as Future<void>;
}
#override
Stream<String> get onTokenRefresh {
return super.noSuchMethod(
Invocation.getter(#onTokenRefresh),
returnValue: const Stream<String>.empty(),
returnValueForMissingStub: const Stream<String>.empty(),
) as Stream<String>;
}
#override
Future<NotificationSettings> requestPermission(
{bool? alert = true,
bool? announcement = false,
bool? badge = true,
bool? carPlay = false,
bool? criticalAlert = false,
bool? provisional = false,
bool? sound = true}) {
return super.noSuchMethod(
Invocation.method(#requestPermission, [], {
#alert: alert,
#announcement: announcement,
#badge: badge,
#carPlay: carPlay,
#criticalAlert: criticalAlert,
#provisional: provisional,
#sound: sound
}),
returnValue: neverEndingFuture<NotificationSettings>(),
returnValueForMissingStub:
neverEndingFuture<NotificationSettings>())
as Future<NotificationSettings>;
}
#override
Future<void> subscribeToTopic(String? topic) {
return super.noSuchMethod(Invocation.method(#subscribeToTopic, [topic]),
returnValue: Future<void>.value(),
returnValueForMissingStub: Future<void>.value()) as Future<void>;
}
#override
Future<void> unsubscribeFromTopic(String? topic) {
return super.noSuchMethod(Invocation.method(#unsubscribeFromTopic, [topic]),
returnValue: Future<void>.value(),
returnValueForMissingStub: Future<void>.value()) as Future<void>;
}
}
class TestFirebaseMessagingPlatform extends FirebaseMessagingPlatform {
TestFirebaseMessagingPlatform() : super();
}
and here is the unit test itself
void main() {
setupFirebaseMessagingMocks();
setUpAll(() async {
await Firebase.initializeApp();
FirebaseMessagingPlatform.instance = kMockMessagingPlatform;
});
test('An example of test', () {
//...
when(kMockMessagingPlatform.getToken(vapidKey: anyNamed('vapidKey')))
.thenAnswer(
(_) => Future.value('DEVICE_ID'),
);
//...
});
}

Related

How to write and read data anywhere by shared_preferences on Flutter 3.7 background isolates?

On Flutter 3.7 platform channels can run on any isolate. So I tried this sample,
import ‘package:flutter/services.dart’;
import ‘package:shared_preferences/shared_preferences.dart’;
void main() {
// Identify the root isolate to pass to the background isolate.
// (API introduced in Flutter 3.7)
RootIsolateToken rootIsolateToken = RootIsolateToken.instance!;
Isolate.spawn(_isolateMain, rootIsolateToken);
}
void _isolateMain(RootIsolateToken rootIsolateToken) async {
// Register the background isolate with the root isolate.
BackgroundIsolateBinaryMessenger
.ensureInitialized(rootIsolateToken);
// You can now use the shared_preferences plugin.
SharedPreferences sharedPreferences =
await SharedPreferences.getInstance();
print(sharedPreferences.getBool(‘isDebug’));
}
I can read from data on shared_preferences in this sample okey. But how can I use this feature anywhere in my app? How can I set or read data using this isolate on initState for example?
Basically you need to implement communication between isolates. You can read more about it here
Here is an example, you can change flutter_secure_storage that i used with shared_preferences package
import 'dart:async';
import 'dart:isolate';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
class CreationEvent {
final RootIsolateToken isolateToken;
final SendPort sendPort;
CreationEvent(this.isolateToken, this.sendPort);
}
class DeletetionEvent {}
class ReadEvent {
final String key;
const ReadEvent(this.key);
}
class ReadResult {
final String key;
final String? content;
const ReadResult(this.key, this.content);
}
class IsolateIO {
IsolateIO._();
final _toBgPort = Completer();
final Map<Object, Completer> _completerMap = {};
Isolate? _isolate;
StreamSubscription? _fromBgListener;
void start() async {
RootIsolateToken rootIsolateToken = RootIsolateToken.instance!;
ReceivePort fromBG = ReceivePort();
_fromBgListener = fromBG.listen((message) {
// setup process
if (message is SendPort) {
_toBgPort.complete(message);
return;
}
if (message is ReadResult) {
_completerMap['read:${message.key}']?.complete(message.content);
_completerMap.remove('read:${message.key}');
}
});
_isolate = await Isolate.spawn(
(CreationEvent data) {
final worker = IsolateWorker(data.isolateToken, data.sendPort);
worker.listen();
},
CreationEvent(rootIsolateToken, fromBG.sendPort),
);
}
Future<String?> readFromStorage(String key) async {
// make sure isolate created with ports
final port = await _toBgPort.future;
// store completer
final completer = Completer<String?>();
_completerMap['read:$key'] = completer;
// send key to be read
port.send(ReadEvent(key));
// return result
return completer.future;
}
void stop() async {
if (_toBgPort.isCompleted) {
final port = await _toBgPort.future;
port.send(DeletetionEvent());
}
_fromBgListener?.cancel();
_isolate?.kill(priority: Isolate.immediate);
}
static final i = IsolateIO._();
}
class IsolateWorker {
final RootIsolateToken rootIsolateToken;
final SendPort toMain;
final FlutterSecureStorage storage;
StreamSubscription? subs;
IsolateWorker(
this.rootIsolateToken,
this.toMain, {
this.storage = const FlutterSecureStorage(
aOptions: AndroidOptions(
encryptedSharedPreferences: true,
),
),
}) {
// Register the background isolate with the root isolate.
BackgroundIsolateBinaryMessenger.ensureInitialized(rootIsolateToken);
}
void listen() {
ReceivePort fromMain = ReceivePort();
toMain.send(fromMain.sendPort);
subs = fromMain.listen((message) => onMessage(message));
}
void onMessage(dynamic message) async {
if (message is DeletetionEvent) {
subs?.cancel();
return;
}
if (message is ReadEvent) {
final rawJson = await storage.read(key: message.key);
toMain.send(ReadResult(message.key, rawJson));
}
}
}
class View extends StatefulWidget {
const View({super.key});
#override
State<View> createState() => _ViewState();
}
class _ViewState extends State<View> {
String username = '';
#override
void initState() {
super.initState();
IsolateIO.i.start();
WidgetsBinding.instance.addPostFrameCallback((_) async {
final name = await IsolateIO.i.readFromStorage('username');
setState(() {
username = name ?? '';
});
});
}
#override
void dispose() {
IsolateIO.i.stop();
super.dispose();
}
#override
Widget build(BuildContext context) {
return SizedBox(
child: Text(username),
);
}
}

My login screen doesn't redirect me to the new screen after I successfully login in flutter

when I try to login it doesn't redirect me to my authenticated page(the state doesn't update) but it stays as it is, it redirects me only after I hot restart the app. I don't know what is wrong with my code.I control the user =null in landing page but landingPage cant return home page.
this is Landing Page
import '/material.dart';
import '/provider.dart';
import '/login-directory/home_page.dart';
import '/login-directory/sign_in_page.dart';
import '/model/user_model.dart' as usr;
import '/viewmodel/viewusermodel.dart';
class LandingPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
final _userModel = Provider.of<UserModel>(context,listen: false);
if (_userModel.state == ViewState.Idle) {
if (_userModel.user == null) {
return SignInPage();//user==null
} else {
return HomePage(user: _userModel.user as usr.User);
}
}
else {
return Scaffold(
body: Center(child: CircularProgressIndicator(),),
);
}
}
}
this is User Repository:
import '/locator.dart';
import '/model/user_model.dart';
import '/services/auth_base.dart';
import '/services/fake_auth_service.dart';
import '/services/firebase_auth_service.dart';
enum AppMode{DEBUG,RELEASE}
class UserRepository implements AuthBase {
FirebaseAuthService _firebaseAuthService = locator<FirebaseAuthService>();
final FakeAuthService _fakeAuthService = locator<FakeAuthService>();
AppMode appMode = AppMode.RELEASE;
#override
Future<User?> currentUser() async {
if (appMode == AppMode.DEBUG) {
return await _fakeAuthService.currentUser();
}
else {
return await _firebaseAuthService.currentUser();
}
}
#override
Future<bool> logOut() async {
if (appMode == AppMode.DEBUG) {
return await _fakeAuthService.logOut();
}
else {
return await _firebaseAuthService.logOut();
}
}
#override
Future<User?> singInAnonymously() async {
if (appMode == AppMode.DEBUG) {
return await _fakeAuthService.singInAnonymously();
}
else {
return await _firebaseAuthService.singInAnonymously();
}
}
#override
Future<User?> signInWithGoogle() async {
if (appMode == AppMode.DEBUG) {
return await _fakeAuthService.singInAnonymously();
}
else {
return await _firebaseAuthService.signInWithGoogle();
}
}
}
Authbase :
abstract class AuthBase{
Future<User?> currentUser();
Future<User?> singInAnonymously();
Future<bool> logOut();
Future<User?> signInWithGoogle();
}
ViewUserModel:
import 'package:flutter/material.dart';
import 'package:/locator.dart';
import 'package:/model/user_model.dart' as usr;
import 'package:/repository/user_repository.dart';
import 'package:/services/auth_base.dart';
enum ViewState{Idle,Busy}
class UserModel with ChangeNotifier implements AuthBase {
ViewState _state = ViewState.Idle;
final UserRepository _userRepository = locator<UserRepository>();
usr.User? _user;
usr.User? get user =>_user;
ViewState get state => _state;
set state(ViewState value) {
_state = value;
notifyListeners(); //
}
UserModel(){
currentUser();
}
#override
Future<usr.User?> currentUser() async {
try {
state = ViewState
.Busy; //kullanıcı verisi cekilirken ekran verisini guncelliyorum
_user = (await _userRepository.currentUser())!;
return _user;
} catch (e) {
debugPrint("ViewModel currentUser is error $e" );
return null;
} finally {
state = ViewState.Idle;
}
}
#override
Future<bool> logOut() async {
try {
state = ViewState.Busy;
bool result =await _userRepository.logOut();
_user= null;
return result;
} catch (e) {
debugPrint("ViewModel currentUser is error $e" );
return false;
} finally {
state = ViewState.Idle;
}
}
#override
Future<usr.User?> singInAnonymously() async {
try {
state = ViewState
.Busy;
_user = (await _userRepository.singInAnonymously())!;
return _user;
} catch (e) {
debugPrint("ViewModel currentUser is error $e");
return null;
} finally {
state = ViewState.Idle;
}
}
#override
Future<usr.User?> signInWithGoogle() async {
try {
state = ViewState
.Busy;
_user = (await _userRepository.signInWithGoogle())!;
return _user;
} catch (e) {
debugPrint("ViewModel currentUser is error $e");
return null;
} finally {
state = ViewState.Idle;
}
}
}
GetIt locator:
GetIt locator=GetIt.instance;
void setupLocator(){
locator.registerLazySingleton(() => FirebaseAuthService());
locator.registerLazySingleton(() => UserRepository());
}
this is main.dart:
import '/viewmodel/viewusermodel.dart';
void main() async{
WidgetsFlutterBinding.ensureInitialized();;
await Firebase.initializeApp();
setupLocator();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title:'Title',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch:Colors.purple,
),
home:ChangeNotifierProvider(
create:(context)=>UserModel(),
child: LandingPage()),
);
}
}

Hive not updating field values

I have a user box where I store some data about the user.
In the main.dart file, I am registering and opening the UserDBAdapter
void main() async {
await Hive.initFlutter();
Hive.registerAdapter(UserDBAdapter());
await Hive.openBox<UserDB>('users');
return runApp(
MyApp(),
);
}
In my Bloc, I have a function which logins the user and then saves true in the isLoggedIn field of the user
class UserBloc extends Bloc<UserEvent, UserState> {
UserDB user = UserDB();
final usersBox = Hive.box<UserDB>('users');
UserRepository userRepository;
UserBloc(this.userRepository) : super(UserInitial()) {
createUser();
verifyUser();
loginUser();
logoutUser();
usersBox.put('user', user);
}
Future<void> loginUser() async {
return on<LoginUser>((event, emit) async {
emit(UserLoading());
await Future.delayed(const Duration(milliseconds: 500));
try {
final result = await userRepository.login(
event.email,
event.password,
);
if (result['success'] == false) {
emit(UserError(result['message']));
} else {
emit(
UserLoggedIn(),
);
user.isLoggedIn = true; // I set it to true here
user.token = result['token'];
user.save(); // Then I save it here. which means the new changes is persisted in the local storage
}
} catch (error) {
emit(
UserError(
error.toString(),
),
);
}
});
}
But my point is that, when I try hot reloading the app, it sends me to the login screen instead of the HomeScreen()
#override
void initState() {
if (Hive.box<UserDB>('users').get('user') == null) {
print('empty'); // This does not print, that means I have a user in the local storage
Hive.box<UserDB>('users').put(
'user',
UserDB(isVerified: false, isLoggedIn: false),
);
}
super.initState();
}
#override
Widget build(BuildContext context) {
final userDB = Hive.box<UserDB>('users').get('user')!;
print(userDB.isLoggedIn); // Here prints false instead of true
if (userDB.isLoggedIn) {
return const NavigationScreen();
} else if (userDB.isVerified && !userDB.isLoggedIn) {
return AnimatedSplashScreen(
splash: const SplashScreen(),
duration: 4000,
nextScreen: const LoginSignUpSwitch(),
);
} else {
return AnimatedSplashScreen(
splash: const SplashScreen(),
duration: 4000,
nextScreen: const WelcomeScreen(),
);
}
}
Below is the UserDB class
import 'package:hive/hive.dart';
part 'user.g.dart';
#HiveType(typeId: 1)
class UserDB extends HiveObject {
UserDB({
this.name,
this.email,
this.isVerified = false,
this.isLoggedIn = false,
this.token,
});
// NAME (0)
#HiveField(0)
String? name;
//EMAIL(1)
#HiveField(1)
String? email;
//IS-VERIFIED(2)
#HiveField(2, defaultValue: false)
bool isVerified;
//IS-LOGGEDIN(3)
#HiveField(3, defaultValue: false)
bool isLoggedIn;
//TOKEN(4)
#HiveField(4)
String? token;
}
NB: I am printing the value of the isLoggedIn in the HomeScreen and it prints true
final userDB = Hive.box<UserDB>('users').get('user');
print(userDB!.isLoggedIn); // Prints true here

Succeeding Bloc are not working after latest Bloc migration

I am using a MultiBlocProvider which is working for all Bloc before I migrate it to v8.0.1. Now, only the first Bloc (SignInBloc) is working.
This is on my main.dart
return MultiBlocProvider(
providers: [
BlocProvider<SignInBloc>(
create: (context) => SignInBloc(
authenticationRepository: authenticationRepository,
userDataRepository: userDataRepository,
),
),
BlocProvider<SignUpBloc>(
create: (context) => SignUpBloc(
authenticationRepository: authenticationRepository,
userDataRepository: userDataRepository,
),
),
Edit: here is my SignInBloc
SignInBloc(
{required this.authenticationRepository,
required this.userDataRepository})
: super(SignInInitialState()) {
on<CheckIfSignedInEvent>(mapCheckIfSignedInEventToState);
}
Future<void> mapCheckIfSignedInEventToState(
CheckIfSignedInEvent event,
Emitter<SignInState> emit,
) async {
try {
bool isSignedIn = await authenticationRepository.checkIfSignedIn();
if (isSignedIn) {
emit(CheckIfSignedInEventCompletedState(true));
} else {
emit(CheckIfSignedInEventCompletedState(false));
}
} catch (e) {
print(e);
emit(CheckIfSignedInEventFailedState());
}
}
I am not sure what to show but here is my SignUpBloc which is similar to my SignInBloc
SignUpBloc(
{required this.authenticationRepository,
required this.userDataRepository})
: super(SignUpInitialState()) {
on<SignUpWithGoogle>(mapSignUpWithGoogleEventToState);
}
Stream<SignUpState> mapSignUpWithGoogleEventToState(
SignUpWithGoogle event,
Emitter<SignUpState> emit,
) async* {
emit(SignUpInProgressState());
try {
User? checkUser = await authenticationRepository.checkIfUserExists();
if (checkUser != null) {
emit(SignUpWithGoogleInitialExistState());
} else {
bool checkDup =
await authenticationRepository.checkIfUserDup(event.name);
if (checkDup == true) {
emit(SignUpWithNameExistState());
} else {
User firebaseUser = await authenticationRepository.signUpWithGoogle();
emit(SignUpWithGoogleInitialCompletedState(firebaseUser));
}
}
} catch (e) {
print(e);
emit(SignUpWithGoogleInitialFailedState());
}
}
My main.dart will call the splash screen which has the declaration of the bloc
late SignInBloc signInBloc;
late SignUpBloc signupBloc;
class _SplashScreenState extends State<SplashScreen> {
#override
void initState() {
super.initState();
signInBloc = BlocProvider.of<SignInBloc>(context);
signupBloc = BlocProvider.of<SignUpBloc>(context);
What I tried to do it to put alot of Print statement in order to check which part is getting called but I don't get why the SignUpBloc is not getting called anymore. Please help. Thanks!
Edit: I tried to debug.
This will trigger my SignInBloc. I'm able to listen to my SignInBloc.
signInBloc.add(CheckIfSignedInEvent());
This should trigger my SignUpBloc. But it doesn't do anything similar to my SignInBloc.
signupBloc.add(SignUpWithGoogle(name: selectedName));
Here's both of my events for comparison:
class CheckIfSignedInEvent extends SignInEvent {
#override
String toString() => 'CheckIfSignedInEvent';
}
class SignUpWithGoogle extends SignUpEvent {
final String name;
SignUpWithGoogle({required this.name});
#override
String toString() => 'SignUpWithGoogleEvent';
}
This is the part where I listen to the states which is both in my splash screen. Only signInBloc is able to listen.
signupBloc.stream.listen((state) {
print('BLOC: signupBloc splash screen init : $state');
});
signInBloc.stream.listen((state) {
print('BLOC: signinBloc splash screen init : $state');
});
It turns out that changing the Stream to Future will fix my issue. async* should also be changed to async
Future<void> mapSignUpWithGoogleEventToState(
SignUpWithGoogle event,
Emitter<SignUpState> emit,
) async {
emit(SignUpInProgressState());
try {
User? checkUser = await authenticationRepository.checkIfUserExists();
if (checkUser != null) {
emit(SignUpWithGoogleInitialExistState());
} else {
bool checkDup =
await authenticationRepository.checkIfUserDup(event.name);
if (checkDup == true) {
emit(SignUpWithNameExistState());
} else {
User firebaseUser = await authenticationRepository.signUpWithGoogle();
emit(SignUpWithGoogleInitialCompletedState(firebaseUser));
}
}
} catch (e) {
print(e);
emit(SignUpWithGoogleInitialFailedState());
}
}

Flutter Pusher Websocket package not working

I have a backend Laravel application that uses Pusher for notifications. I would like to show notifications in my Flutter app (both iOS and Android). I found that https://pub.dev/packages/pusher_websocket_flutter/ package has the best score, but I can't get it to work. I've followed this tutorial, and I get no errors (whatever I put for my APP_KEY, which must be wrong), but I never get anything shown.
Has anyone managed to get this working, or should I switch to firebase?
This is my pusher_service.dart:
import 'package:flutter/services.dart';
import 'package:pusher_websocket_flutter/pusher.dart';
import 'dart:async';
class PusherService {
Event lastEvent;
String lastConnectionState;
Channel channel;
StreamController<String> _eventData = StreamController<String>();
Sink get _inEventData => _eventData.sink;
Stream get eventStream => _eventData.stream;
Future<void> initPusher() async {
try {
await Pusher.init('XXX', PusherOptions(cluster: 'XX'), enableLogging: true);
print("Pusher initialized");
}
on PlatformException catch (e) {
print(e.message);
}
}
void connectPusher() {
Pusher.connect(
onConnectionStateChange: (ConnectionStateChange connectionState) async {
lastConnectionState = connectionState.currentState;
print("Pusher connected");
}, onError: (ConnectionError e) {
print("Error: ${e.message}");
});
}
Future<void> subscribePusher(String channelName) async {
channel = await Pusher.subscribe(channelName);
print("Pusher subscribed to channel");
}
void unSubscribePusher(String channelName) {
Pusher.unsubscribe(channelName);
}
void bindEvent(String eventName) {
channel.bind(eventName, (last) {
final String data = last.data;
_inEventData.add(data);
});
print("Pusher data binded");
}
void unbindEvent(String eventName) {
channel.unbind(eventName);
_eventData.close();
}
Future<void> firePusher(String channelName, String eventName) async {
await initPusher();
connectPusher();
await subscribePusher(channelName);
bindEvent(eventName);
}
}
My pusher_test.dart:
import 'package:flutter/material.dart';
import 'package:chalet/services/pusher_service.dart';
import 'package:pusher/pusher.dart';
import 'dart:async';
class PusherTest extends StatefulWidget {
#override
_PusherTestState createState() => _PusherTestState();
}
class _PusherTestState extends State<PusherTest> {
PusherService pusherService = PusherService();
#override
void initState() {
pusherService = PusherService();
pusherService.firePusher('public', 'create');
testPusher();
super.initState();
}
#override
void dispose() {
pusherService.unbindEvent('create');
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: StreamBuilder(
stream: pusherService.eventStream,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return CircularProgressIndicator();
}
return Container(
child: Text(snapshot.data),
);
},
),
),
);
}
}
I've checked and my snapshot.connectionState is always waiting.
Try this:
import 'dart:async';
import 'dart:convert';
import 'dart:developer';
import 'package:pusher_client/pusher_client.dart';
//instantiate Pusher Class
class PusherController {
static final PusherController _pusherController =
PusherController._internal();
factory PusherController() {
return _pusherController;
}
PusherController._internal();
PusherClient pusher;
Channel channel;
StreamController<String> _eventData = StreamController<String>.broadcast();
Sink get _inEventData => _eventData.sink;
Stream get eventStream => _eventData.stream;
String channelName = "";
String prevChannelName = "";
String eventName = "";
void initPusher() {
PusherOptions options = PusherOptions(
cluster: "eu",
);
pusher = new PusherClient("key", options,
autoConnect: true, enableLogging: true);
}
void setChannelName(String name) {
channelName = name;
print("channelName: ${channelName}");
}
void setEventName(String name) {
eventName = name;
print("eventName: ${eventName}");
}
void subscribePusher() {
channel = pusher.subscribe(channelName);
pusher.onConnectionStateChange((state) {
log("previousState: ${state.previousState}, currentState: ${state.currentState}");
});
pusher.onConnectionError((error) {
log("error: ${error.message}");
});
//Bind to listen for events called and sent to channel
channel.bind(eventName, (PusherEvent event) {
print("xxxxxxxxx From pusher xxxxxxxxx");
print('xxxxx This is Event name - $eventName xxxx');
print('xxxxx This is Event gotten - ${event.data} xxx');
_inEventData.add(event.data);
prevChannelName = eventName;
});
}
void connectPusher() {
pusher.connect();
}
void disconnectPusher() async {
await channel.unbind(eventName);
await pusher.disconnect();
}
}
Then use streamBuilder and stream from evenStream.