Abd*_*her 0 notifications push-notification dart pusher flutter
我正在使用Pusher_channels_flutter包来获取实时通知。
我想在Flutter中使用Pusher的Private通道。
await pusher.subscribe(channelName: "private-chat.5");
但我得到了这个错误:
日志:错误:PlatformException(错误,无法订阅私有或存在通道,因为尚未设置授权者。在连接到 Pusher 之前调用 PusherOptions.setAuthorizer(),null,java.lang.IllegalStateException:无法订阅私有或存在通道因为还没有设置Authorizer,在连接Pusher之前调用PusherOptions.setAuthorizer()
当我添加onAuthorizer功能时,我是这样添加的:
dynamic onAuthorizer(String channelName, String socketId, dynamic options) async {
return {
"auth": "foo:bar",
"channel_data": '{"user_id": 1}',
"shared_secret": "foobar"
};
}
Run Code Online (Sandbox Code Playgroud)
但我得到了这个错误:
日志:onError:订阅身份验证数据中的密钥无效:“令牌”代码:null 异常:null
auth我应该在 onAuthorizer 映射的和channel_data和键的值中放入什么shared_secret?
我的完整代码:
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:pusher_channels_flutter/pusher_channels_flutter.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
PusherChannelsFlutter pusher = PusherChannelsFlutter.getInstance();
String _log = 'output:\n';
final _apiKey = TextEditingController();
final _cluster = TextEditingController();
final _channelName = TextEditingController();
final _eventName = TextEditingController();
final _channelFormKey = GlobalKey<FormState>();
final _eventFormKey = GlobalKey<FormState>();
final _listViewController = ScrollController();
final _data = TextEditingController();
void log(String text) {
print("LOG: $text");
setState(() {
_log += text + "\n";
Timer(
const Duration(milliseconds: 100),
() => _listViewController
.jumpTo(_listViewController.position.maxScrollExtent));
});
}
@override
void initState() {
super.initState();
initPlatformState();
}
void onConnectPressed() async {
if (!_channelFormKey.currentState!.validate()) {
return;
}
// Remove keyboard
FocusScope.of(context).requestFocus(FocusNode());
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString("apiKey", "mykey"); //_apiKey.text);
prefs.setString("cluster", "eu"); // _cluster.text);
prefs.setString("channelName", "private-chat.5"); //_channelName.text);
try {
await pusher.init(
apiKey: "mykey", //_apiKey.text,
cluster: "eu", //_cluster.text,
onConnectionStateChange: onConnectionStateChange,
onError: onError,
onSubscriptionSucceeded: onSubscriptionSucceeded,
onEvent: onEvent,
onSubscriptionError: onSubscriptionError,
onDecryptionFailure: onDecryptionFailure,
onMemberAdded: onMemberAdded,
onMemberRemoved: onMemberRemoved,
// authEndpoint: "<Your Authendpoint Url>",
onAuthorizer: onAuthorizer,
// authParams: {
// 'params': { 'foo': 'bar' },
// 'headers': { 'X-CSRF-Token': 'SOME_CSRF_TOKEN' }
// }
);
await pusher.subscribe(channelName: "private-chat.5"); // _channelName.text,);
await pusher.connect();
} catch (e) {
log("ERROR: $e");
}
}
dynamic onAuthorizer(String channelName, String socketId, dynamic options) async {
return {
"auth": "foo:bar",
"channel_data": '{"user_id": 1}',
"shared_secret": "foobar"
};
}
Future<void> pusherDiconnect() async {
await pusher.unsubscribe(channelName: "private-chat.5"); //_channelName.text,);
await pusher.disconnect();
print("pusherDiconnect");
}
void onConnectionStateChange(dynamic currentState, dynamic previousState) {
log("Connection: $currentState");
}
void onError(String message, int? code, dynamic e) {
log("onError: $message code: $code exception: $e");
}
void onEvent(PusherEvent event) {
log("onEvent: $event");
}
void onSubscriptionSucceeded(String channelName, dynamic data) {
log("onSubscriptionSucceeded: $channelName data: $data");
final me = pusher.getChannel(channelName)?.me;
log("Me: $me");
}
void onSubscriptionError(String message, dynamic e) {
log("onSubscriptionError: $message Exception: $e");
}
void onDecryptionFailure(String event, String reason) {
log("onDecryptionFailure: $event reason: $reason");
}
void onMemberAdded(String channelName, PusherMember member) {
log("onMemberAdded: $channelName user: $member");
}
void onMemberRemoved(String channelName, PusherMember member) {
log("onMemberRemoved: $channelName user: $member");
}
void onTriggerEventPressed() async {
var eventFormValidated = _eventFormKey.currentState!.validate();
if (!eventFormValidated) {
return;
}
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString("eventName", _eventName.text);
prefs.setString("data", _data.text);
pusher.trigger(PusherEvent(
channelName: _channelName.text,
eventName: _eventName.text,
data: _data.text));
}
// Platform messages are asynchronous, so we initialize in an async method.
Future<void> initPlatformState() async {
// 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;
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
_apiKey.text = prefs.getString("apiKey") ?? '';
_cluster.text = prefs.getString("cluster") ?? 'eu';
_channelName.text = prefs.getString("channelName") ?? 'my-channel';
_eventName.text = prefs.getString("eventName") ?? 'client-event';
_data.text = prefs.getString("data") ?? 'test';
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text(pusher.connectionState == 'DISCONNECTED'
? 'Pusher Channels Example'
: _channelName.text),
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: ListView(
controller: _listViewController,
scrollDirection: Axis.vertical,
shrinkWrap: true,
children: <Widget>[
if (pusher.connectionState != 'CONNECTED')
Form(
key: _channelFormKey,
child: Column(children: <Widget>[
TextFormField(
controller: _apiKey,
validator: (String? value) {
return (value != null && value.isEmpty)
? 'Please enter your API key.'
: null;
},
decoration:
const InputDecoration(labelText: 'API Key'),
),
TextFormField(
controller: _cluster,
validator: (String? value) {
return (value != null && value.isEmpty)
? 'Please enter your cluster.'
: null;
},
decoration: const InputDecoration(
labelText: 'Cluster',
),
),
TextFormField(
controller: _channelName,
validator: (String? value) {
return (value != null && value.isEmpty)
? 'Please enter your channel name.'
: null;
},
decoration: const InputDecoration(
labelText: 'Channel',
),
),
ElevatedButton(
onPressed: onConnectPressed,
child: const Text('Connect'),
)
]))
else
Form(
key: _eventFormKey,
child: Column(children: <Widget>[
ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: pusher
.channels[_channelName.text]?.members.length,
itemBuilder: (context, index) {
final member = pusher
.channels[_channelName.text]!.members.values
.elementAt(index);
return ListTile(
title: Text(member.userInfo.toString()),
subtitle: Text(member.userId));
}),
TextFormField(
controller: _eventName,
validator: (String? value) {
return (value != null && value.isEmpty)
? 'Please enter your event name.'
: null;
},
decoration: const InputDecoration(
labelText: 'Event',
),
),
TextFormField(
controller: _data,
decoration: const InputDecoration(
labelText: 'Data',
),
),
ElevatedButton(
onPressed: onTriggerEventPressed,
child: const Text('Trigger Event'),
),
ElevatedButton(
onPressed: (){
pusherDiconnect();
},
child: const Text('pusher Diconnect'),
),
]),
),
SingleChildScrollView(
scrollDirection: Axis.vertical, child: Text(_log)),
]),
),
),
);
}
}
Run Code Online (Sandbox Code Playgroud)
小智 5
将 onAuthorizer 回调替换为以下代码:
getSignature(String value) {
var key = utf8.encode('<your-pusher-app-secret>');
var bytes = utf8.encode(value);
var hmacSha256 = Hmac(sha256, key); // HMAC-SHA256
var digest = hmacSha256.convert(bytes);
print("HMAC signature in string is: $digest");
return digest;
}
dynamic onAuthorizer(String channelName, String socketId, dynamic options) {
return {
"auth": "<your-pusher-key>:${getSignature("$socketId:$channelName")}",
};
}
Run Code Online (Sandbox Code Playgroud)
PS:使用https://pub.dev/packages/crypto来访问Hmac方法
请参阅参考:https ://pusher.com/docs/channels/library_auth_reference/auth-signatures/
我在使用订阅私人频道时遇到问题pusher_channels_flutter
如果其他人遇到同样的问题,我将在这里粘贴我的代码。
这些代码非常适合我,请检查您token使用的用户onAuthorizer
在 initState 初始化推送器
@override
void initState() {
super.initState();
_initPusher();
}
Run Code Online (Sandbox Code Playgroud)
并可处置
@override
void dispose() {
_pusher.disconnect();
_pusher.unsubscribe(channelName: "private-notification.${_user!.id}");
super.dispose();
}
Run Code Online (Sandbox Code Playgroud)
这是_initPusher()函数
Future<void> _initPusher() async {
_user = Provider.of<ProfileProvider>(context, listen: false).getUser;
_pusher = PusherChannelsFlutter.getInstance();
try {
await _pusher.init(
apiKey: pusherKey,
cluster: "eu",
onConnectionStateChange: onConnectionStateChange,
onError: onError,
onSubscriptionSucceeded: onSubscriptionSucceeded,
onEvent: onEvent,
onSubscriptionError: onSubscriptionError,
onDecryptionFailure: onDecryptionFailure,
onMemberAdded: onMemberAdded,
onMemberRemoved: onMemberRemoved,
//authEndpoint: "https://my-website.com/broadcasting/auth",
onAuthorizer: onAuthorizer,
);
await _pusher.subscribe(channelName: "private-notification.${_user!.id}");
await _pusher.connect();
} catch (e) {
print("error in initialization: $e");
}
}
Run Code Online (Sandbox Code Playgroud)
我onAuthorizer()不知道如何管理参数,但它工作得很好。
这是onAuthorizer()函数
dynamic onAuthorizer(
String channelName, String socketId, dynamic options) async {
String token = await Store.read("token");
var authUrl = "$basePath/broadcasting/auth";
var result = await http.post(
Uri.parse(authUrl),
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Bearer ${token}',
},
body: 'socket_id=' + socketId + '&channel_name=' + channelName,
);
var json = jsonDecode(result.body);
return json;
}
Run Code Online (Sandbox Code Playgroud)
对于authUrl我的后端来说,Laravel如果有人在后端使用 laravel,则broadcasting/auth在输入 时必须有一个名为的路由php artisan route:list。否则修复后端的问题。
这些是其余的功能
void onError(String message, int? code, dynamic e) {
print("onError: $message code: $code exception: $e");
}
void onConnectionStateChange(dynamic currentState, dynamic previousState) {
print("Connection: $currentState");
}
void onMemberRemoved(String channelName, PusherMember member) {
print("onMemberRemoved: $channelName member: $member");
}
void onMemberAdded(String channelName, PusherMember member) {
print("onMemberAdded: $channelName member: $member");
}
void onSubscriptionSucceeded(String channelName, dynamic data) {
print("onSubscriptionSucceeded: $channelName data: $data");
}
void onSubscriptionError(String message, dynamic e) {
print("onSubscriptionError: $message Exception: $e");
}
void onEvent(PusherEvent event) {
print("onEvent: $event");
Provider.of<NotificationProvider>(context, listen: false)
.setNotificationCount(1);
}
void onDecryptionFailure(String event, String reason) {
print("onDecryptionFailure: $event reason: $reason");
}
Run Code Online (Sandbox Code Playgroud)
我希望它能解决某人的问题。
| 归档时间: |
|
| 查看次数: |
3556 次 |
| 最近记录: |