🎬 短剧模块
版本 PanGrowth

🃏 短剧预览卡片

短剧预览卡片以 16:9 视频预览位的形式在任意页面展示短剧首集或推荐片段,支持自动播放、静音控制、循环播放等能力,可与播放页、聚合页联动实现完整短剧体验。插件同时提供 NativeView 组件API 控制 两条集成路径。

🚪 接入方式概览

方式推荐场景实现特点
NativeView 组件(推荐)Flutter 页面内嵌卡片,信息流/网格展示使用 DramaCardNativeView,自动管理创建/销毁,可配合 DramaCardControllerDramaCardListener
API 调用需要在原生 Activity/ViewController 展示或精确控制生命周期使用 createDramaCard / showDramaCard / destroyDramaCard 主动控制

注意:两种方式的配置对象相同,都使用 DramaCardConfig

✅ 方式一:DramaCardNativeView(推荐)

DramaCardNativeView 通过 PlatformView 内嵌原生短剧卡片,Flutter 层像普通 Widget 一样使用。

基础示例

import 'package:flutter/material.dart';
import 'package:pangrowth_content/pangrowth_content.dart';

class DramaCardWidget extends StatelessWidget {
  final int dramaId;

  const DramaCardWidget({
    super.key,
    required this.dramaId,
  });

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      width: 220,
      height: 220 * 9 / 16,  // 保持 16:9 比例
      child: DramaCardNativeView(
        config: DramaCardConfig(
          dramaId: dramaId,
          width: 220,
          isAutoPlay: true,
          isMuteDefault: true,
          isLooping: false,
        ),
      ),
    );
  }
}

结合 DramaCardListener

使用 DramaCardListener 监听卡片生命周期事件:

DramaCardNativeView(
  config: DramaCardConfig(
    dramaId: 12345,
    width: 220,
    isAutoPlay: true,
    isMuteDefault: true,
  ),
  listener: DramaCardListener(
    onCardReady: (cardId) {
      debugPrint('卡片就绪: $cardId');
    },
    onDataLoaded: (drama) {
      debugPrint('短剧数据加载完成: ${drama?.title}');
    },
    onCardClicked: (cardId) {
      // 用户点击卡片,可跳转到播放页
      Navigator.push(
        context,
        MaterialPageRoute(
          builder: (context) => DramaPlayerPage(dramaId: dramaId),
        ),
      );
    },
    onCardError: (error) {
      debugPrint('卡片加载失败: $error');
    },
  ),
)

结合 DramaCardController

当需要控制卡片播放/暂停时,使用 DramaCardController:

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

  @override
  State<CardControlDemo> createState() => _CardControlDemoState();
}

class _CardControlDemoState extends State<CardControlDemo> {
  final _controller = DramaCardController();

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        SizedBox(
          width: 220,
          height: 220 * 9 / 16,
          child: DramaCardNativeView(
            config: DramaCardConfig(
              dramaId: 12345,
              width: 220,
              isAutoPlay: false,  // 不自动播放,由控制器控制
            ),
            controller: _controller,
          ),
        ),
        const SizedBox(height: 16),
        Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
              onPressed: () => _controller.play(),
              child: const Text('播放'),
            ),
            const SizedBox(width: 8),
            ElevatedButton(
              onPressed: () => _controller.pause(),
              child: const Text('暂停'),
            ),
            const SizedBox(width: 8),
            ElevatedButton(
              onPressed: () => _controller.setMute(true),
              child: const Text('静音'),
            ),
          ],
        ),
      ],
    );
  }
}

控制器方法

方法说明
play()播放卡片
pause()暂停卡片
stop()停止卡片
setMute(bool mute)设置静音状态
refresh()刷新卡片数据
reportShow(int duration)上报曝光埋点(单位毫秒)
isPlaying()查询播放状态
getDramaInfo()获取短剧信息
getGid()获取短剧 GID
getTitle()获取短剧标题

配置参数:DramaCardConfig

参数类型平台说明
必填参数
dramaIdintAndroid / iOS短剧 ID
通用可选参数
widthint?Android / iOS卡片宽度(dp),高度自动按 16:9 计算
isMuteDefaultbool?Android / iOS是否默认静音(建议信息流场景设为 true)
isLoopingbool?Android / iOS是否循环播放
isAutoPlaybool?Android / iOS是否自动播放
iOS 专用参数
hideActionUIbool?@ios隐藏操作 UI,可自行叠加自定义按钮
hidePlayButtonbool?@ios隐藏播放按钮
hideMuteButtonbool?@ios隐藏静音按钮
Android 专用参数
hideSoundButtonbool?@android隐藏声音按钮
hideReplayButtonbool?@android隐藏重播按钮

生命周期管理

DramaCardNativeView 自动管理卡片生命周期:

  • 创建: Widget initState 时自动创建原生视图
  • 显示: Widget build 时自动显示
  • 销毁: Widget dispose 时自动销毁原生视图

无需手动调用任何销毁方法,Widget 销毁时会自动释放资源。

信息流嵌入示例

class DramaCardList extends StatelessWidget {
  final List<int> dramaIds = [12345, 12346, 12347];

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: dramaIds.length,
      itemBuilder: (context, index) {
        return Container(
          margin: const EdgeInsets.symmetric(
            horizontal: 16,
            vertical: 8,
          ),
          child: DramaCardNativeView(
            config: DramaCardConfig(
              dramaId: dramaIds[index],
              width: MediaQuery.of(context).size.width.toInt() - 32,
              isAutoPlay: true,
              isMuteDefault: true,  // 信息流默认静音
              isLooping: true,      // 循环播放吸引注意
              hidePlayButton: true, // iOS: 隐藏播放按钮
              hideSoundButton: true,// Android: 隐藏声音按钮
            ),
            listener: DramaCardListener(
              onCardClicked: (cardId) {
                // 点击跳转播放页
                _openDramaPlayer(context, dramaIds[index]);
              },
            ),
          ),
        );
      },
    );
  }
}

🧭 方式二:API 控制

适合需要精确控制卡片生命周期的场景,如原生 Activity/ViewController 中展示。

完整生命周期演示

import 'package:flutter/material.dart';
import 'package:pangrowth_content/pangrowth_content.dart';

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

  @override
  State<DramaCardAPIDemo> createState() => _DramaCardAPIDemoState();
}

class _DramaCardAPIDemoState extends State<DramaCardAPIDemo> {
  String? _cardId;

  Future<void> _createAndShow() async {
    // 1. 创建卡片
    final config = DramaCardConfig(
      dramaId: 12345,
      width: 350,
      isAutoPlay: true,
      isMuteDefault: false,
    );

    final result = await PangrowthContent.createDramaCard(config);

    if (result['success'] == true) {
      _cardId = result['cardId'] as String;
      debugPrint('创建成功: $_cardId');

      // 2. 显示卡片(全屏弹窗)
      await PangrowthContent.showDramaCard(_cardId!);
      debugPrint('显示成功');
    }
  }

  Future<void> _destroy() async {
    if (_cardId != null) {
      await PangrowthContent.destroyDramaCard(_cardId!);
      debugPrint('销毁成功');
      setState(() {
        _cardId = null;
      });
    }
  }

  @override
  void dispose() {
    // 清理资源
    if (_cardId != null) {
      PangrowthContent.destroyDramaCard(_cardId!);
    }
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('API方式演示')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
              onPressed: _cardId == null ? _createAndShow : null,
              child: const Text('创建并显示'),
            ),
            const SizedBox(height: 16),
            ElevatedButton(
              onPressed: _cardId != null ? _destroy : null,
              child: const Text('销毁'),
            ),
          ],
        ),
      ),
    );
  }
}

控制接口

方法说明
createDramaCard(config)创建卡片,返回 cardId
showDramaCard(cardId)在原生页面打开卡片容器(Android 启动 Activity,iOS 弹出 ViewController)
playDramaCard(cardId)播放卡片
pauseDramaCard(cardId)暂停卡片
stopDramaCard(cardId)停止卡片
setDramaCardMute(cardId, mute)切换静音状态
reportDramaCardShow(cardId, duration)上报曝光埋点(单位毫秒)
refreshDramaCard(cardId)重新拉取卡片数据
destroyDramaCard(cardId)释放卡片并关闭容器
isDramaCardPlaying(cardId)查询播放状态
getDramaCardInfo(cardId)获取短剧信息
getDramaCardGid(cardId)获取短剧 GID
getDramaCardTitle(cardId)获取短剧标题

业务场景示例

信息流卡片

final config = DramaCardConfig(
  dramaId: 12345,
  width: 320,
  isMuteDefault: true,  // 默认静音,避免打扰
  isAutoPlay: true,     // 自动播放
  isLooping: true,      // 循环播放
  hidePlayButton: true, // @ios 隐藏播放按钮
  hideActionUI: true,   // @ios 隐藏操作UI
  hideSoundButton: true,// @android 隐藏声音按钮
);

final result = await PangrowthContent.createDramaCard(config);

沉浸式播放

final config = DramaCardConfig(
  dramaId: 12345,
  width: 375,
  isMuteDefault: false,  // 有声播放
  isAutoPlay: true,      // 自动播放
  isLooping: false,      // 不循环
  hidePlayButton: false, // 保留播放控制
  hideActionUI: false,   // 保留操作UI
);

final result = await PangrowthContent.createDramaCard(config);

📡 事件监听

卡片生命周期及播放状态通过 DramaCardListener 监听:

可用回调:

  • onCardReady(String cardId) - 卡片创建完成
  • onDataLoaded(DramaInfo? drama) - 短剧数据加载完成
  • onCardClicked(DramaInfo? drama) - 用户点击卡片
  • onPlaybackStateChanged(bool isPlaying) - 播放状态变化
  • onCardError(String error) - 卡片加载失败
  • onCardDisposed() - 卡片已销毁

🌍 平台差异

Android

  • 卡片 View 由 IDJXElement 提供,可直接嵌入任意 ViewGroup
  • NativeView 方式:通过 PlatformView 嵌入
  • API 方式:通过专用 Activity 托管全屏展示
  • width 以 dp 传入,高度自动按 16:9 计算

iOS

  • DJXPlayletCard 直接作为 UIView 使用,天然支持嵌入
  • NativeView 方式:通过 PlatformView 嵌入
  • API 方式:通过 ViewController 全屏弹出
  • width 参数自动计算 frame(x:0, y:0, width:width, height:width*9/16)
  • 设置 hideActionUI 后可自行叠加自定义 UI

💡 最佳实践

推荐使用 NativeView 方式

除非有特殊需求,否则推荐使用 DramaCardNativeView,理由:

  • ✅ 自动管理生命周期,减少内存泄漏风险
  • ✅ 代码更简洁,符合 Flutter 开发习惯
  • ✅ 与 Flutter Widget 树完美集成
  • ✅ 支持 DramaCardControllerDramaCardListener
  • ✅ 可直接嵌入信息流、网格等布局

配置参数选择建议

  1. 只传递需要修改的参数:SDK 会为未传递的参数使用原生默认值
  2. 信息流场景:
    • isMuteDefault: true - 默认静音
    • isLooping: true - 循环播放
    • hidePlayButton: true / hideSoundButton: true - 隐藏控件
  3. 详情页场景:
    • isMuteDefault: false - 有声播放
    • isLooping: false - 不循环
    • 保留所有控件

性能优化建议

  1. 列表场景:配合 AutomaticKeepAliveClientMixin 避免重复创建
  2. 及时销毁:页面退出时确保 dispose
  3. 合理设置尺寸:建议宽度 > 180dp,保持 16:9 比例

错误处理

// NativeView方式
DramaCardNativeView(
  config: config,
  listener: DramaCardListener(
    onCardError: (error) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('加载失败: $error')),
      );
    },
  ),
)

// API方式
try {
  final result = await PangrowthContent.createDramaCard(config);
  if (result['success'] != true) {
    debugPrint('创建失败');
  }
} catch (e) {
  debugPrint('异常: $e');
}

🔗 相关文档


💡 提示: 短剧卡片需要有效的短剧内容源。建议合理设置卡片尺寸(宽度 > 180dp)和播放参数(静音、循环)以获得最佳用户体验。

需要进一步协助?

与 LightCore 技术顾问沟通,获取商业化策略与集成支持。