sy_rtc_flutter_sdk 1.2.0 copy "sy_rtc_flutter_sdk: ^1.2.0" to clipboard
sy_rtc_flutter_sdk: ^1.2.0 copied to clipboard

SY RTC Flutter SDK - A Flutter plugin for real-time audio and video communication.

example/lib/main.dart

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:sy_rtc_flutter_sdk/sy_rtc_flutter_sdk.dart';
import 'app_config.dart';
import 'live_control_page.dart';
import 'token_service.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'SY RTC Flutter 示例',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
        useMaterial3: true,
      ),
      home: const RtcDemoPage(),
    );
  }
}

class RtcDemoPage extends StatefulWidget {
  const RtcDemoPage({super.key});

  @override
  State<RtcDemoPage> createState() => _RtcDemoPageState();
}

class _RtcDemoPageState extends State<RtcDemoPage> {
  final _engine = SyRtcEngine();
  AppConfig? _config;
  TokenService? _tokenService;

  final _apiBaseController = TextEditingController(
    text: 'https://your-rtc-server.com',
  );
  final _signalingController = TextEditingController(
    text: 'wss://your-rtc-server.com/ws/signaling',
  );
  final _appIdController = TextEditingController(text: 'your_app_id');
  final _jwtController = TextEditingController();
  final _channelIdController = TextEditingController(text: 'channel_001');
  final _uidController = TextEditingController(text: 'user_001');
  final _tokenController = TextEditingController(text: '');

  String _status = '未初始化';
  bool _initialized = false;
  bool _inChannel = false;
  bool _muted = false;

  StreamSubscription<SyUserJoinedEvent>? _userJoinedSub;
  StreamSubscription<SyUserOfflineEvent>? _userOfflineSub;
  StreamSubscription<SyVolumeIndicationEvent>? _volumeSub;

  @override
  void initState() {
    super.initState();
    _setupListeners();
  }

  void _setupListeners() {
    _userJoinedSub = _engine.onUserJoined.listen((e) {
      if (mounted) setState(() => _status = '用户加入: ${e.uid}');
    });
    _userOfflineSub = _engine.onUserOffline.listen((e) {
      if (mounted) setState(() => _status = '用户离开: ${e.uid}');
    });
    _volumeSub = _engine.onVolumeIndication.listen((e) {
      if (mounted && e.speakers.isNotEmpty) {
        setState(() => _status = '音量: ${e.speakers.length} 人');
      }
    });
  }

  Future<void> _saveAndInit() async {
    final apiBase = _apiBaseController.text.trim();
    final signaling = _signalingController.text.trim();
    final appId = _appIdController.text.trim();
    if (apiBase.isEmpty || signaling.isEmpty || appId.isEmpty) {
      setState(() => _status = '请填写 API 地址、信令地址和 AppId');
      return;
    }
    setState(() => _status = '正在初始化...');
    try {
      _config = AppConfig(
        apiBaseUrl: apiBase,
        signalingUrl: signaling,
        appId: appId,
        jwt: _jwtController.text.trim().isEmpty ? null : _jwtController.text.trim(),
      );
      _tokenService = TokenService(_config!);

      await _engine.init(
        _config!.appId,
        apiBaseUrl: _config!.apiBaseUrl,
        signalingUrl: _config!.signalingUrl,
      );

      final hasVoice = await _engine.hasVoiceFeature();
      final hasLive = await _engine.hasLiveFeature();
      setState(() {
        _initialized = true;
        _status = '初始化成功 | 语聊=$hasVoice, 直播=$hasLive';
      });
    } catch (e) {
      setState(() => _status = '初始化失败: $e');
    }
  }

  Future<void> _fetchToken() async {
    if (_tokenService == null) {
      setState(() => _status = '请先保存并初始化');
      return;
    }
    final channelId = _channelIdController.text.trim();
    final uid = _uidController.text.trim();
    if (channelId.isEmpty || uid.isEmpty) {
      setState(() => _status = '请填写频道 ID 和用户 ID');
      return;
    }
    setState(() => _status = '正在拉取 Token...');
    try {
      final token = await _tokenService!.fetchRtcToken(
        channelId: channelId,
        uid: uid,
      );
      _tokenController.text = token;
      setState(() => _status = 'Token 已拉取');
    } catch (e) {
      setState(() => _status = '拉取 Token 失败: $e');
    }
  }

  Future<void> _joinChannel() async {
    if (!_initialized) {
      setState(() => _status = '请先保存并初始化');
      return;
    }
    final channelId = _channelIdController.text.trim();
    final uid = _uidController.text.trim();
    final token = _tokenController.text.trim();
    if (channelId.isEmpty || uid.isEmpty || token.isEmpty) {
      setState(() => _status = '请填写频道 ID、用户 ID 和 Token(可点击拉取 Token)');
      return;
    }
    setState(() => _status = '正在加入...');
    try {
      await _engine.join(channelId, uid, token);
      setState(() {
        _inChannel = true;
        _status = '已加入 $channelId';
      });
    } catch (e) {
      setState(() => _status = '加入失败: $e');
    }
  }

  Future<void> _leaveChannel() async {
    if (!_inChannel) return;
    setState(() => _status = '正在离开...');
    try {
      await _engine.leave();
      setState(() {
        _inChannel = false;
        _status = '已离开频道';
      });
    } catch (e) {
      setState(() => _status = '离开失败: $e');
    }
  }

  Future<void> _toggleMute() async {
    if (!_inChannel) return;
    try {
      _muted = !_muted;
      await _engine.muteLocalAudio(_muted);
      setState(() => _status = _muted ? '已静音' : '已取消静音');
    } catch (e) {
      _muted = !_muted;
      setState(() => _status = '静音操作失败: $e');
    }
  }

  Future<void> _openLiveControl() async {
    if (!_initialized || _config == null) {
      setState(() => _status = '请先保存并初始化');
      return;
    }
    final hasLive = await _engine.hasLiveFeature();
    if (!hasLive) {
      setState(() => _status = '当前 AppId 未开通直播功能');
      return;
    }
    final channelId = _channelIdController.text.trim();
    final uid = _uidController.text.trim();
    final token = _tokenController.text.trim();
    if (channelId.isEmpty || uid.isEmpty || token.isEmpty) {
      setState(() => _status = '请填写频道 ID、用户 ID 和 Token 后再打开直播控制');
      return;
    }
    Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) => LiveControlPage(
          engine: _engine,
          channelId: channelId,
          uid: uid,
          token: token,
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('SY RTC Flutter 示例'),
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
      ),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            _section('配置', [
              _field('API 基础 URL', _apiBaseController, hint: 'https://api.example.com'),
              _field('信令 URL', _signalingController, hint: 'wss://api.example.com/ws/signaling'),
              _field('AppId', _appIdController),
              _field('JWT(可选,用于拉取 Token)', _jwtController, obscure: true),
              ElevatedButton(
                onPressed: _saveAndInit,
                child: const Text('保存并初始化'),
              ),
            ]),
            const SizedBox(height: 16),
            _section('状态', [
              Text(_status, style: const TextStyle(fontSize: 14)),
            ]),
            const SizedBox(height: 16),
            _section('频道与 Token', [
              _field('频道 ID', _channelIdController),
              _field('用户 ID', _uidController),
              _field('RTC Token', _tokenController, obscure: true),
              Row(
                children: [
                  ElevatedButton(
                    onPressed: _fetchToken,
                    child: const Text('拉取 Token'),
                  ),
                  const SizedBox(width: 8),
                  Text('需先填 JWT 并初始化', style: TextStyle(fontSize: 12, color: Colors.grey.shade700)),
                ],
              ),
            ]),
            const SizedBox(height: 16),
            _section('频道操作', [
              Row(
                children: [
                  Expanded(
                    child: ElevatedButton(
                      onPressed: _inChannel ? null : _joinChannel,
                      style: ElevatedButton.styleFrom(backgroundColor: Colors.green),
                      child: const Text('加入频道'),
                    ),
                  ),
                  const SizedBox(width: 8),
                  Expanded(
                    child: ElevatedButton(
                      onPressed: _inChannel ? _leaveChannel : null,
                      style: ElevatedButton.styleFrom(backgroundColor: Colors.orange),
                      child: const Text('离开频道'),
                    ),
                  ),
                ],
              ),
              const SizedBox(height: 8),
              ElevatedButton(
                onPressed: _inChannel ? _toggleMute : null,
                child: Text(_muted ? '取消静音' : '静音'),
              ),
            ]),
            const SizedBox(height: 16),
            _section('直播', [
              ElevatedButton.icon(
                onPressed: _openLiveControl,
                icon: const Icon(Icons.live_tv),
                label: const Text('直播控制面板'),
                style: ElevatedButton.styleFrom(
                  backgroundColor: Colors.blue,
                  foregroundColor: Colors.white,
                ),
              ),
            ]),
          ],
        ),
      ),
    );
  }

  Widget _section(String title, List<Widget> children) {
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              title,
              style: const TextStyle(
                fontSize: 18,
                fontWeight: FontWeight.bold,
              ),
            ),
            const SizedBox(height: 12),
            ...children,
          ],
        ),
      ),
    );
  }

  Widget _field(String label, TextEditingController controller, {String? hint, bool obscure = false}) {
    return Padding(
      padding: const EdgeInsets.only(bottom: 8),
      child: TextField(
        controller: controller,
        decoration: InputDecoration(
          labelText: label,
          hintText: hint,
          border: const OutlineInputBorder(),
          isDense: true,
        ),
        obscureText: obscure,
      ),
    );
  }

  @override
  void dispose() {
    _userJoinedSub?.cancel();
    _userOfflineSub?.cancel();
    _volumeSub?.cancel();
    _apiBaseController.dispose();
    _signalingController.dispose();
    _appIdController.dispose();
    _jwtController.dispose();
    _channelIdController.dispose();
    _uidController.dispose();
    _tokenController.dispose();
    _engine.dispose();
    super.dispose();
  }
}
0
likes
145
points
387
downloads

Publisher

unverified uploader

Weekly Downloads

SY RTC Flutter SDK - A Flutter plugin for real-time audio and video communication.

Repository (GitHub)

Documentation

API reference

License

MIT (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on sy_rtc_flutter_sdk

Packages that implement sy_rtc_flutter_sdk