diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index a4d797c..ea62918 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,4 +1,11 @@ + + + + + + + data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory UserInfo.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'UserInfo', + package: const $pb.PackageName(_omitMessageNames ? '' : 'my_project'), + createEmptyInstance: create) + ..aI(1, _omitFieldNames ? '' : 'id') + ..aOS(2, _omitFieldNames ? '' : 'name') + ..aOS(3, _omitFieldNames ? '' : 'email') + ..aOB(4, _omitFieldNames ? '' : 'isAdmin') + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + UserInfo clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + UserInfo copyWith(void Function(UserInfo) updates) => + super.copyWith((message) => updates(message as UserInfo)) as UserInfo; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static UserInfo create() => UserInfo._(); + @$core.override + UserInfo createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static UserInfo getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static UserInfo? _defaultInstance; + + @$pb.TagNumber(1) + $core.int get id => $_getIZ(0); + @$pb.TagNumber(1) + set id($core.int value) => $_setSignedInt32(0, value); + @$pb.TagNumber(1) + $core.bool hasId() => $_has(0); + @$pb.TagNumber(1) + void clearId() => $_clearField(1); + + @$pb.TagNumber(2) + $core.String get name => $_getSZ(1); + @$pb.TagNumber(2) + set name($core.String value) => $_setString(1, value); + @$pb.TagNumber(2) + $core.bool hasName() => $_has(1); + @$pb.TagNumber(2) + void clearName() => $_clearField(2); + + @$pb.TagNumber(3) + $core.String get email => $_getSZ(2); + @$pb.TagNumber(3) + set email($core.String value) => $_setString(2, value); + @$pb.TagNumber(3) + $core.bool hasEmail() => $_has(2); + @$pb.TagNumber(3) + void clearEmail() => $_clearField(3); + + @$pb.TagNumber(4) + $core.bool get isAdmin => $_getBF(3); + @$pb.TagNumber(4) + set isAdmin($core.bool value) => $_setBool(3, value); + @$pb.TagNumber(4) + $core.bool hasIsAdmin() => $_has(3); + @$pb.TagNumber(4) + void clearIsAdmin() => $_clearField(4); +} + +const $core.bool _omitFieldNames = + $core.bool.fromEnvironment('protobuf.omit_field_names'); +const $core.bool _omitMessageNames = + $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/lib/generated/user.pbenum.dart b/lib/generated/user.pbenum.dart new file mode 100644 index 0000000..65d18b5 --- /dev/null +++ b/lib/generated/user.pbenum.dart @@ -0,0 +1,11 @@ +// This is a generated file - do not edit. +// +// Generated from user.proto. + +// @dart = 3.3 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names +// ignore_for_file: curly_braces_in_flow_control_structures +// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes +// ignore_for_file: non_constant_identifier_names, prefer_relative_imports diff --git a/lib/generated/user.pbjson.dart b/lib/generated/user.pbjson.dart new file mode 100644 index 0000000..90aa1b1 --- /dev/null +++ b/lib/generated/user.pbjson.dart @@ -0,0 +1,32 @@ +// This is a generated file - do not edit. +// +// Generated from user.proto. + +// @dart = 3.3 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names +// ignore_for_file: curly_braces_in_flow_control_structures +// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes +// ignore_for_file: non_constant_identifier_names, prefer_relative_imports +// ignore_for_file: unused_import + +import 'dart:convert' as $convert; +import 'dart:core' as $core; +import 'dart:typed_data' as $typed_data; + +@$core.Deprecated('Use userInfoDescriptor instead') +const UserInfo$json = { + '1': 'UserInfo', + '2': [ + {'1': 'id', '3': 1, '4': 1, '5': 5, '10': 'id'}, + {'1': 'name', '3': 2, '4': 1, '5': 9, '10': 'name'}, + {'1': 'email', '3': 3, '4': 1, '5': 9, '10': 'email'}, + {'1': 'is_admin', '3': 4, '4': 1, '5': 8, '10': 'isAdmin'}, + ], +}; + +/// Descriptor for `UserInfo`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List userInfoDescriptor = $convert.base64Decode( + 'CghVc2VySW5mbxIOCgJpZBgBIAEoBVICaWQSEgoEbmFtZRgCIAEoCVIEbmFtZRIUCgVlbWFpbB' + 'gDIAEoCVIFZW1haWwSGQoIaXNfYWRtaW4YBCABKAhSB2lzQWRtaW4='); diff --git a/lib/main.dart b/lib/main.dart index 244a702..720db06 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'test_page.dart'; void main() { runApp(const MyApp()); @@ -109,6 +110,16 @@ class _MyHomePageState extends State { '$_counter', style: Theme.of(context).textTheme.headlineMedium, ), + const SizedBox(height: 20), + ElevatedButton( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute(builder: (context) => const TestPage()), + ); + }, + child: const Text('Go to WebSocket & WebRTC Test'), + ), ], ), ), diff --git a/lib/test_page.dart b/lib/test_page.dart new file mode 100644 index 0000000..12c9201 --- /dev/null +++ b/lib/test_page.dart @@ -0,0 +1,131 @@ +import 'package:flutter/material.dart'; +import 'package:web_socket_channel/web_socket_channel.dart'; +import 'package:flutter_webrtc/flutter_webrtc.dart'; + +class TestPage extends StatefulWidget { + const TestPage({super.key}); + + @override + State createState() => _TestPageState(); +} + +class _TestPageState extends State { + final TextEditingController _controller = TextEditingController(); + WebSocketChannel? _channel; + final List _messages = []; + + final RTCVideoRenderer _localRenderer = RTCVideoRenderer(); + + @override + void initState() { + super.initState(); + initRenderers(); + } + + Future initRenderers() async { + await _localRenderer.initialize(); + } + + @override + void dispose() { + _channel?.sink.close(); + _localRenderer.dispose(); + super.dispose(); + } + + void _connectWebSocket() { + final url = _controller.text.trim(); + if (url.isNotEmpty) { + setState(() { + _channel = WebSocketChannel.connect(Uri.parse(url)); + _messages.clear(); + }); + } + } + + void _sendMessage() { + if (_channel != null) { + _channel!.sink.add('Hello from Flutter!'); + } + } + + Future _startWebRTC() async { + final Map mediaConstraints = { + 'audio': true, + 'video': { + 'facingMode': 'user', + }, + }; + + try { + MediaStream stream = await navigator.mediaDevices.getUserMedia(mediaConstraints); + _localRenderer.srcObject = stream; + setState(() {}); + } catch (e) { + print('Error getting user media: $e'); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('WebSocket & WebRTC Test'), + ), + body: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + TextField( + controller: _controller, + decoration: const InputDecoration( + labelText: 'WebSocket URL (e.g. ws://echo.websocket.org)', + ), + ), + Row( + children: [ + ElevatedButton( + onPressed: _connectWebSocket, + child: const Text('Connect'), + ), + const SizedBox(width: 10), + ElevatedButton( + onPressed: _sendMessage, + child: const Text('Send Message'), + ), + ], + ), + Expanded( + child: StreamBuilder( + stream: _channel?.stream, + builder: (context, snapshot) { + if (snapshot.hasData) { + _messages.add(snapshot.data.toString()); + } + return ListView.builder( + itemCount: _messages.length, + itemBuilder: (context, index) { + return ListTile( + title: Text(_messages[index]), + ); + }, + ); + }, + ), + ), + const Divider(), + const Text('WebRTC Test:'), + ElevatedButton( + onPressed: _startWebRTC, + child: const Text('Start Camera'), + ), + SizedBox( + height: 200, + child: RTCVideoView(_localRenderer), + ), + ], + ), + ), + ); + } +} diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index e71a16d..3f48831 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -6,6 +6,10 @@ #include "generated_plugin_registrant.h" +#include void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) flutter_webrtc_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterWebRTCPlugin"); + flutter_web_r_t_c_plugin_register_with_registrar(flutter_webrtc_registrar); } diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 2e1de87..53574ba 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -3,9 +3,11 @@ # list(APPEND FLUTTER_PLUGIN_LIST + flutter_webrtc ) list(APPEND FLUTTER_FFI_PLUGIN_LIST + jni ) set(PLUGIN_BUNDLED_LIBRARIES) diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index cccf817..4f68d10 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,6 +5,8 @@ import FlutterMacOS import Foundation +import flutter_webrtc func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + FlutterWebRTCPlugin.register(with: registry.registrar(forPlugin: "FlutterWebRTCPlugin")) } diff --git a/protos/user.proto b/protos/user.proto new file mode 100644 index 0000000..d9522f4 --- /dev/null +++ b/protos/user.proto @@ -0,0 +1,11 @@ +syntax = "proto3"; + +// 尽管 Dart 不强制要求 package,但建议填写以防冲突 +package my_project; + +message UserInfo { + int32 id = 1; + string name = 2; + string email = 3; + bool is_admin = 4; +} \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index df3d562..4eabb49 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,6 +1,14 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + args: + dependency: transitive + description: + name: args + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.7.0" async: dependency: transitive description: @@ -33,6 +41,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.1.2" + code_assets: + dependency: transitive + description: + name: code_assets + sha256: bf394f466ba9205f1812a0433b392d6af280f155f56651eda7c18cc32ed493b8 + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.2.1" collection: dependency: transitive description: @@ -41,6 +57,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.19.1" + crypto: + dependency: transitive + description: + name: crypto + sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.0.7" cupertino_icons: dependency: "direct main" description: @@ -49,6 +73,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.0.9" + dart_webrtc: + dependency: transitive + description: + name: dart_webrtc + sha256: f6d615bddea5e458ce180a914f3055c234ffb52fb7397a51b3491e76d6d7edb2 + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.8.1" fake_async: dependency: transitive description: @@ -57,6 +89,22 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.3.3" + ffi: + dependency: transitive + description: + name: ffi + sha256: "6d7fd89431262d8f3125e81b50d3847a091d846eafcd4fdb88dd06f36d705a45" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.2.0" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.1" flutter: dependency: "direct main" description: flutter @@ -75,6 +123,46 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_webrtc: + dependency: "direct main" + description: + name: flutter_webrtc + sha256: "0f89dee7f4c35dab5611f351c3897a3bfe9f7057720cc7da209b52455af4316e" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.5.2" + hooks: + dependency: transitive + description: + name: hooks + sha256: "9a62a50b50b769a737bc0a8ff381f333529df3ab746b2f6b02e83760231455ba" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.0.2" + jni: + dependency: transitive + description: + name: jni + sha256: c2230682d5bc2362c1c9e8d3c7f406d9cbba23ab3f2e203a025dd47e0fb2e68f + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.0.0" + jni_flutter: + dependency: transitive + description: + name: jni_flutter + sha256: "8b59e590786050b1cd866677dddaf76b1ade5e7bc751abe04b86e84d379d3ba6" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.0.1" + js: + dependency: transitive + description: + name: js + sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.7.2" leak_tracker: dependency: transitive description: @@ -107,6 +195,22 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "6.1.0" + logger: + dependency: transitive + description: + name: logger + sha256: "25aee487596a6257655a1e091ec2ae66bc30e7af663592cc3a27e6591e05035c" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.7.0" + logging: + dependency: transitive + description: + name: logging + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.3.0" matcher: dependency: transitive description: @@ -131,6 +235,22 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.18.0" + objective_c: + dependency: transitive + description: + name: objective_c + sha256: "6cb691c686fa2838c6deb34980d426145c2a5d537491cb83d463c33cdbc726ed" + url: "https://pub.flutter-io.cn" + source: hosted + version: "9.4.1" + package_config: + dependency: transitive + description: + name: package_config + sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.2.0" path: dependency: transitive description: @@ -139,6 +259,94 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.9.1" + path_provider: + dependency: transitive + description: + name: path_provider + sha256: a7f4874f987173da295a61c181b8ee71dab59b332a486b391babf26a1b884825 + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.1.6" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: "69cbd515a62b94d32a7944f086b2f82b4ac40a1d45bebfc00813a430ab2dabcd" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.3.1" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: "2a376b7d6392d80cd3705782d2caa734ca4727776db0b6ec36ef3f1855197699" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.6.0" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: "58c2005f147315b11e9b4a7bc889cd5203e250cba8e3f012dae259b4972b5c16" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.2.2" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "484838772624c3a4b94f1e44a3e19897fee738f2d5c4ce448443b0417f7c9dda" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.1.3" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.3.0" + platform: + dependency: transitive + description: + name: platform + sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.1.6" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.1.8" + protobuf: + dependency: "direct main" + description: + name: protobuf + sha256: "68645b24e0716782e58948f8467fd42a880f255096a821f9e7d0ec625b00c84d" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.1.0" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.2.0" + record_use: + dependency: transitive + description: + name: record_use + sha256: "2551bd8eecfe95d14ae75f6021ad0248be5c27f138c2ec12fcb52b500b3ba1ed" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.6.0" sky_engine: dependency: transitive description: flutter @@ -176,6 +384,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.4.1" + synchronized: + dependency: transitive + description: + name: synchronized + sha256: "93b153dcb6a26dcddee6ca087dd634b53e38c10b5aa163e8e49501a776456153" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.4.1" term_glyph: dependency: transitive description: @@ -192,6 +408,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "0.7.11" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.4.0" vector_math: dependency: transitive description: @@ -208,6 +432,54 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "15.2.0" + web: + dependency: transitive + description: + name: web + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.1" + web_socket: + dependency: transitive + description: + name: web_socket + sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.0.1" + web_socket_channel: + dependency: "direct main" + description: + name: web_socket_channel + sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8 + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.0.3" + webrtc_interface: + dependency: transitive + description: + name: webrtc_interface + sha256: c6f100eac5057d9a817a60473126f9828c796d42884d498af4f339c97b21014f + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.5.1" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.0" + yaml: + dependency: transitive + description: + name: yaml + sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.1.3" sdks: dart: ">=3.12.1 <4.0.0" - flutter: ">=3.18.0-18.0.pre.54" + flutter: ">=3.38.4" diff --git a/pubspec.yaml b/pubspec.yaml index af6f78b..591c958 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -31,6 +31,10 @@ dependencies: flutter: sdk: flutter + web_socket_channel: ^3.0.3 + protobuf: ^3.1.0 + flutter_webrtc: ^1.4.1 + # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.8 diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 8b6d468..e8559e4 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,6 +6,9 @@ #include "generated_plugin_registrant.h" +#include void RegisterPlugins(flutter::PluginRegistry* registry) { + FlutterWebRTCPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FlutterWebRTCPlugin")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index b93c4c3..f3cdbfe 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,9 +3,11 @@ # list(APPEND FLUTTER_PLUGIN_LIST + flutter_webrtc ) list(APPEND FLUTTER_FFI_PLUGIN_LIST + jni ) set(PLUGIN_BUNDLED_LIBRARIES)