把ChatGPT加入Flutter开发,会有怎样的体验?
来源 | OSCHINA 社区
作者 | 声网
原文链接:https://my.oschina.net/agora/blog/8367471
前言
谷歌紧急推出 “Bard” 对抗 ChatGPT 微软发布新 Bing 集成 ChatGPT 复旦发布首个类 ChatGPT 模型 MOSS 国内阿里、百度、昆仑万维、网易、京东都开始新一轮 AI 军备
与以往的统计模型不行,ChatGPT 不是那种「一切都从语料统计里学习」的 AI,相反 ChatGPT 具备有临场学习的能力,业内称之为 in-context learning ,这也是为什么 ChatGPT 可以在上下文中学习的原因。
PS:在此之前有人通过指示在 ChatGPT 界面下实现了一个虚拟机,虽然这是一个极端的例子,但是可以很直观地感受到:「ChatGPT 对我们开发的影响是肉眼可见」。
基于 ChatGPT 开发
01 开发之前
「开发一个直播 app,是使用第三方 SDK 好还是自己从 0 开发好」?
有没有发现,在获取资料的检索方式上,ChatGPT 确实比搜索引擎更直观且高效。
「移动端哪个跨平台框架更适合做直播」?
关于注册获取 App ID 等步骤这里就省略了,毕竟目前这部分 ChatGPT 也无能为力。
02 开始开发
为什么关键词是「视频通话」?因为它比直播场景更精准简单,生成的代码更靠谱(经过提问测试),而基于视频通话部分,后面我们可以快速拓展为互动直播场景;而指定版本是为了避免 AI 使用旧版本 API。
从这里也可以感觉到 ,ChatGPT 不是一个单纯的完全只会基于语料答复整合的 AI 。
采用 createAgoraRtcEngine 和 initialize 创建和初始化 RtcEngine 将 setEventHandler 修改为最新的 registerEventHandler 将 AgoraRenderWidget 修改为 AgoraVideoView
class VideoCallPage extends StatefulWidget {
final String channelName;
const VideoCallPage({Key? key, required this.channelName}) : super(key: key);
@override
_VideoCallPageState createState() => _VideoCallPageState();
}
class _VideoCallPageState extends State<VideoCallPage> {
late RtcEngine _engine;
bool _localUserJoined = false;
bool _remoteUserJoined = false;
int? rUid;
@override
void initState() {
super.initState();
initAgora();
}
@override
void dispose() {
_engine.leaveChannel();
super.dispose();
}
Future<void> initAgora() async {
await [Permission.microphone, Permission.camera].request();
_engine = createAgoraRtcEngine();
await _engine.initialize(RtcEngineContext(
appId: config.appId,
channelProfile: ChannelProfileType.channelProfileLiveBroadcasting,
));
_engine.registerEventHandler(RtcEngineEventHandler(
onJoinChannelSuccess: (RtcConnection connection, int elapsed) {
setState(() {
_localUserJoined = true;
});
},
onUserJoined: (connection, remoteUid, elapsed) {
setState(() {
_remoteUserJoined = true;
rUid = remoteUid;
});
},
onUserOffline: (RtcConnection connection, int remoteUid,
UserOfflineReasonType reason) {
setState(() {
_remoteUserJoined = false;
rUid = null;
});
},
));
await _engine.enableVideo();
await _engine.startPreview();
await _engine.joinChannel(
token: config.token,
channelId: widget.channelName,
uid: config.uid,
options: const ChannelMediaOptions(
channelProfile: ChannelProfileType.channelProfileLiveBroadcasting,
clientRoleType: ClientRoleType.clientRoleBroadcaster,
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("VideoCallPage"),),
body: Center(
child: Stack(
children: [
_remoteUserJoined ? _remoteVideoView(rUid) : _placeholderView(),
_localUserJoined ? _localVideoView() : _placeholderView(),
],
),
),
);
}
Widget _placeholderView() {
return Container(
color: Colors.black,
);
}
Widget _remoteVideoView(id) {
return AgoraVideoView(
controller: VideoViewController.remote(
rtcEngine: _engine,
canvas: VideoCanvas(uid: id),
connection: RtcConnection(channelId: widget.channelName),
),
);
}
Widget _localVideoView() {
return Positioned(
right: 16,
bottom: 16,
width: 100,
height: 160,
child: AgoraVideoView(
controller: VideoViewController(
rtcEngine: _engine,
canvas: const VideoCanvas(uid: 0),
),
),
);
}
}
红色方块是后期加上的打码~
03 进阶开发
这里为什么还强制写 agora_rtc_engine 6.1.0 ?因为如果不写,默认可能会输出 4.x 版本的老 API。
这里是我自己加的粉色,不然都是白色会糊成一坨,不得不说 ChatGPT 在绘制能力上 “很抽象”。
这就是 ChatGPT 在每次会话上下文里学习的表现。
源码在后面。
class StarPaint extends StatelessWidget {
@override
Widget build(BuildContext context) {
return CustomPaint(
painter: HeartPainter(),
size: Size(50, 50),
);
}
}
class StarPaint extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.red
..style = PaintingStyle.fill;
final path = Path();
final halfWidth = size.width / 2;
final halfHeight = size.height / 2;
final radius = halfWidth;
path.moveTo(halfWidth, halfHeight + radius);
path.arcToPoint(
Offset(halfWidth + radius, halfHeight),
radius: Radius.circular(radius),
clockwise: true,
);
path.arcToPoint(
Offset(halfWidth, halfHeight - radius),
radius: Radius.circular(radius),
clockwise: true,
);
path.arcToPoint(
Offset(halfWidth - radius, halfHeight),
radius: Radius.circular(radius),
clockwise: true,
);
path.arcToPoint(
Offset(halfWidth, halfHeight + radius),
radius: Radius.circular(radius),
clockwise: true,
);
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(covariant StarPaint oldDelegate) {
return false;
}
}
class StarPaint extends StatelessWidget {
@override
Widget build(BuildContext context) {
return CustomPaint(
painter: HeartPainter(),
size: Size(50, 50),
);
}
}
class StarPaint extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.red
..style = PaintingStyle.fill;
final path = Path();
final halfWidth = size.width / 2;
final halfHeight = size.height / 2;
final radius = halfWidth;
path.moveTo(halfWidth, halfHeight + radius);
path.arcToPoint(
Offset(halfWidth + radius, halfHeight),
radius: Radius.circular(radius),
clockwise: true,
);
path.arcToPoint(
Offset(halfWidth, halfHeight - radius),
radius: Radius.circular(radius),
clockwise: true,
);
path.arcToPoint(
Offset(halfWidth - radius, halfHeight),
radius: Radius.circular(radius),
clockwise: true,
);
path.arcToPoint(
Offset(halfWidth, halfHeight + radius),
radius: Radius.circular(radius),
clockwise: true,
);
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(covariant StarPaint oldDelegate) {
return false;
}
}
onStreamMessage: (RtcConnection connection, int remoteUid, int streamId,
Uint8List data, int length, int sentTs) {
var message = utf8.decode(data);
if (message == "兔子") {
showDialog(
context: context,
builder: (context) {
return AnimaWidget(Rabbit());
});
} else if (message == "星星") {
showDialog(
context: context,
builder: (context) {
return Center(
child: AnimaWidget(StarPaint()),
);
});
}
Future.delayed(Duration(seconds: 3), () {
Navigator.pop(context);
});
},
Future<void> _onPressSend() async {
try {
final streamId = await _engine.createDataStream(
const DataStreamConfig(syncWithAudio: false, ordered: false));
var txt = (Random().nextInt(10) % 2 == 0) ? "星星" : "兔子";
final data = Uint8List.fromList(utf8.encode(txt));
await _engine.sendStreamMessage(
streamId: streamId, data: data, length: data.length);
} catch (e) {
print(e);
}
}
class AnimaWidget extends StatefulWidget {
final Widget child;
const AnimaWidget(this.child);
@override
State<AnimaWidget> createState() => _AnimaWidgetState();
}
class _AnimaWidgetState extends State<AnimaWidget> {
double animaScale = 1;
@override
void initState() {
super.initState();
Future.delayed(Duration(seconds: 1), () {
animaScale = 5;
setState(() {});
});
}
@override
Widget build(BuildContext context) {
return AnimatedScale(
scale: animaScale,
duration: Duration(seconds: 1),
curve: Curves.bounceIn,
child: Container(child: widget.child));
}
}
我怎么知道这个插件?肯定也是问 ChatGPT 的啊~
04 最后
「当你抱怨 ChatGPT 鬼话连篇满嘴跑火车的时候,这可能有点像你看到一只猴子在沙滩上用石头写下 1+1=3。它确实算错了,但这不是重点。它有一天会算对的。」
END
这里有最新开源资讯、软件更新、技术干货等内容
点这里 ↓↓↓ 记得 关注✔ 标星⭐ 哦
微信扫码关注该文公众号作者
戳这里提交新闻线索和高质量文章给我们。
来源: qq
点击查看作者最近其他文章