🎬 短剧模块
版本 PanGrowth

📺 短剧聚合页

短剧聚合页提供短剧聚合浏览、分类筛选、置顶推荐、播放解锁和商业化能力。插件同时支持 NativeView 组件(推荐)与 API 控制 两种接入方式,可根据业务形态灵活选择。

🚪 接入方式概览

方式推荐场景实现特点
NativeView 组件(推荐)Flutter 页面内嵌短剧内容,追求最快集成DramaHomeNativeView 自动管理创建/销毁,Flutter 布局友好
API 调用需要在原生 Activity / ViewController 中展示,或兼容历史流程createDramaHome / showDramaHome / destroyDramaHome 主动控制生命周期

✅ 方式一:使用 DramaHomeNativeView(推荐)

DramaHomeNativeView 通过 PlatformView 内嵌原生聚合页,Flutter 端仅需像普通 Widget 一样使用。

基础用法

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

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('短剧推荐')),
      body: DramaHomeNativeView(
        config: const DramaHomeConfig(
          showBackBtn: false,             // 已有 AppBar,不需要原生返回按钮
          freeEpisodesCount: 3,            // 默认提供 3 集免费内容
          unlockEpisodesCountUsingAd: 2,   // 激励广告解锁 2 集
        ),
      ),
    );
  }
}

监听聚合页生命周期

通过 DramaHomeListener 获取组件创建、销毁及错误信息,便于埋点或自定义 UI:

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

  @override
  State<DramaHomeWithListenerPage> createState() => _DramaHomeWithListenerPageState();
}

class _DramaHomeWithListenerPageState extends State<DramaHomeWithListenerPage> {
  String? _viewId;
  bool _isLoading = true;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('短剧推荐')),
      body: Stack(
        children: [
          DramaHomeNativeView(
            config: const DramaHomeConfig(
              showBackBtn: false,
              freeEpisodesCount: 3,
              unlockEpisodesCountUsingAd: 2,
            ),
            listener: DramaHomeListener(
              onHomeReady: (viewId) {
                setState(() {
                  _viewId = viewId;
                  _isLoading = false;
                });
                debugPrint('✅ 聚合页就绪: $viewId');
              },
              onHomeDisposed: () {
                debugPrint('🗑️ 聚合页已释放');
              },
              onHomeError: (error) {
                setState(() {
                  _isLoading = false;
                });
                debugPrint('❌ 聚合页加载失败: $error');
                ScaffoldMessenger.of(context).showSnackBar(
                  const SnackBar(content: Text('聚合页加载失败,请稍后重试')),
                );
              },
            ),
          ),
          if (_isLoading)
            const Center(
              child: CircularProgressIndicator(),
            ),
        ],
      ),
    );
  }
}

TabBar 场景示例

在 TabBar 中使用短剧聚合页,配合其他内容页面:

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

  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: 3,
      child: Scaffold(
        appBar: AppBar(
          title: const Text('首页'),
          bottom: const TabBar(
            tabs: [
              Tab(icon: Icon(Icons.home), text: '推荐'),
              Tab(icon: Icon(Icons.video_library), text: '短剧'),
              Tab(icon: Icon(Icons.person), text: '我的'),
            ],
          ),
        ),
        body: TabBarView(
          children: [
            const Center(child: Text('推荐页面')),
            // 短剧聚合页
            DramaHomeNativeView(
              config: const DramaHomeConfig(
                showBackBtn: false,  // TabBar中不需要返回按钮
                freeEpisodesCount: 5,
                unlockEpisodesCountUsingAd: 2,
              ),
            ),
            const Center(child: Text('我的页面')),
          ],
        ),
      ),
    );
  }
}

自定义置顶短剧

通过 topDramaIdtopDramaIds 参数指定置顶推荐的短剧:

// 单个置顶短剧
DramaHomeNativeView(
  config: const DramaHomeConfig(
    topDramaId: 1008,  // 置顶短剧ID
    freeEpisodesCount: 3,
  ),
)

// 多个置顶短剧
DramaHomeNativeView(
  config: const DramaHomeConfig(
    topDramaIds: ['1008', '1009', '1010'],  // 多个置顶短剧ID
    freeEpisodesCount: 3,
  ),
)

自定义播放详情页 UI

通过各种 hide* 参数控制播放详情页的 UI 元素显示:

// 简洁模式:隐藏大部分UI元素
DramaHomeNativeView(
  config: const DramaHomeConfig(
    freeEpisodesCount: 3,
    // 隐藏播放详情页的各种UI元素
    hideTopInfo: true,        // 隐藏顶部信息区
    hideBottomInfo: false,    // 保留底部交互按钮
    hideBack: true,           // 隐藏返回按钮(使用Flutter返回)
    hideMore: true,           // 隐藏更多按钮
    hideLikeIcon: false,      // 保留点赞按钮
    hideFavorIcon: false,     // 保留收藏按钮
    hideRewardDialog: false,  // 使用SDK默认解锁弹窗
  ),
)

自定义解锁弹窗

隐藏 SDK 默认解锁弹窗,使用 Flutter 自定义弹窗:

DramaHomeNativeView(
  config: const DramaHomeConfig(
    freeEpisodesCount: 3,
    unlockEpisodesCountUsingAd: 2,
    hideRewardDialog: true,  // ⚠️ 隐藏SDK默认弹窗,使用自定义弹窗
  ),
  listener: DramaHomeListener(
    onHomeReady: (viewId) {
      debugPrint('聚合页就绪: $viewId');
    },
    // 监听短剧点击、搜索等事件...
  ),
)

生命周期管理

DramaHomeNativeView 自动管理聚合页生命周期:

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

生命周期约束

  • 单实例约束:插件内部限制同一时刻仅允许存在一个聚合页实例,创建新的 DramaHomeNativeView 前需确保旧实例已被销毁
  • 平台视图限制:PlatformView 由原生渲染,避免在其上方叠加 Positioned/Stack 等复杂布局
  • 销毁策略DramaHomeNativeViewdispose 阶段会自动调用 PangrowthContent.destroyDramaHome,如果在外层缓存了 viewId,可在自定义流程中重复调用(幂等)

🧭 方式二:使用 API 控制聚合页

API 方式用于历史项目兼容或需要原生容器承载的场景。插件内部已将 createDramaHome / showDramaHome 标记为 @Deprecated,如无特殊需求,仍建议优先选择 NativeView 方案。

调用流程

  1. createDramaHome(config: ...) 创建聚合页,获得 widgetId
  2. showDramaHome(widgetId) 打开原生聚合页页面(Android 启动 Activity,iOS 推入控制器)
  3. 页面结束时调用 destroyDramaHome(widgetId) 释放资源

参考实现

class DramaHomeLauncher {
  String? _widgetId;

  Future<void> openDramaHome() async {
    final result = await PangrowthContent.createDramaHome(
      config: const DramaHomeConfig(
        showBackBtn: true,
        freeEpisodesCount: 3,
        unlockEpisodesCountUsingAd: 2,
      ),
    );

    if (result['success'] != true) {
      debugPrint('创建聚合页失败: $result');
      return;
    }

    _widgetId = result['widgetId'] as String;
    debugPrint('✅ 创建聚合页成功: $_widgetId');

    await PangrowthContent.showDramaHome(_widgetId!);
    debugPrint('✅ 显示聚合页成功');
  }

  Future<void> dispose() async {
    if (_widgetId != null) {
      await PangrowthContent.destroyDramaHome(_widgetId!);
      debugPrint('✅ 销毁聚合页成功');
      _widgetId = null;
    }
  }
}

// 使用示例
final launcher = DramaHomeLauncher();

// 打开聚合页
await launcher.openDramaHome();

// 页面退出时销毁
await launcher.dispose();

destroyDramaHome 为幂等调用,即使聚合页已关闭再次调用也不会抛出异常。


🧩 配置参数详解(DramaHomeConfig

以下参数两种接入方式均可使用,未显式赋值时会使用 SDK 默认值。

聚合页界面配置

参数类型平台支持说明默认值
showChangeBtnbool?🤖 Android是否展示热门推荐区域的"换一换"按钮null(SDK默认)
showPageTitlebool?🤖 Android是否展示页面标题栏(iOS 使用系统导航栏)null(SDK默认)
showBackBtnbool?✅ 双端是否展示聚合页返回按钮,Flutter 层会自动映射到 iOS 导航栏null(SDK默认)
topOffsetint?🤖 Android调整聚合页顶部内容偏移量(dp),适配自定义标题栏null

内容配置

参数类型平台支持说明默认值
topDramaIdint?✅ 双端指定单个置顶短剧 ID,展示在热门推荐最左侧null
topDramaIdsList<String>?✅ 双端指定多个置顶短剧 ID,优先级高于 topDramaIdnull

播放控制

参数类型平台支持说明约束默认值
freeEpisodesCountint?✅ 双端免费连看集数≤20 集且不超过总集数的 20%(取其中较大值)null(SDK默认)
unlockEpisodesCountUsingAdint?✅ 双端激励广告解锁的集数单个激励视频最多解锁 10 集null(SDK默认)

播放详情页 UI 控制

参数类型平台支持说明默认值
hideTopInfobool?✅ 双端隐藏播放详情页顶部信息区null(SDK默认)
hideBottomInfobool?✅ 双端隐藏播放详情页底部交互按钮区null(SDK默认)
hideBackbool?✅ 双端隐藏播放详情页内部的返回按钮null(SDK默认)
hideMorebool?✅ 双端隐藏"更多"按钮(举报等入口)null(SDK默认)
hideLikeIconbool?✅ 双端隐藏点赞按钮null(SDK默认)
hideFavorIconbool?✅ 双端隐藏收藏按钮null(SDK默认)
hideRewardDialogbool?✅ 双端隐藏 SDK 内置激励解锁弹窗,便于自定义 UInull(SDK默认)
enableInfiniteScrollbool?✅ 双端开启后自动轮播下一部短剧(iOS 内部取反处理)null(SDK默认)

iOS 导航栏配置

参数类型平台支持说明默认值
isShowNavigationItemBackButtonbool?🍎 iOS控制导航栏返回按钮显示,showBackBtn 会自动映射到此参数null(SDK默认)
isShowNavigationItemTitlebool?🍎 iOS控制导航栏标题显示null(SDK默认)

其他配置

参数类型平台支持说明默认值
extraMap<String, dynamic>?✅ 双端自定义扩展字段,将在事件回调中透传null

📡 事件监听

组件级事件(DramaHomeListener

通过 DramaHomeListener 获取组件的生命周期事件,适合更新 Flutter UI 状态:

DramaHomeNativeView(
  config: const DramaHomeConfig(freeEpisodesCount: 3),
  listener: DramaHomeListener(
    onHomeReady: (viewId) {
      debugPrint('✅ 聚合页就绪: $viewId');
      // 可在此处执行埋点上报等操作
    },
    onHomeDisposed: () {
      debugPrint('🗑️ 聚合页已释放');
    },
    onHomeError: (error) {
      debugPrint('❌ 聚合页加载失败: $error');
      // 显示错误提示
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('聚合页加载失败,请稍后重试')),
      );
    },
  ),
)

可用回调

  • onHomeReady(String viewId) - 聚合页创建完成
  • onHomeDisposed() - 聚合页已销毁
  • onHomeError(String error) - 聚合页加载失败
  • onItemClick(DramaInfo drama) - 用户点击短剧卡片
  • onSearch(String keyword) - 触发搜索
  • onCategorySwitch(String category) - 分类标签切换

💡 使用场景与实践

场景1:首页短剧推荐

在首页展示短剧聚合页,作为主要内容入口:

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('首页'),
        actions: [
          IconButton(
            icon: const Icon(Icons.search),
            onPressed: () {
              // 跳转到搜索页
            },
          ),
        ],
      ),
      body: DramaHomeNativeView(
        config: const DramaHomeConfig(
          showBackBtn: false,  // 首页不需要返回按钮
          freeEpisodesCount: 5,
          unlockEpisodesCountUsingAd: 2,
        ),
      ),
    );
  }
}

场景2:VIP专区

为 VIP 用户提供更多免费集数和无限轮播:

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('VIP专区')),
      body: DramaHomeNativeView(
        config: const DramaHomeConfig(
          showBackBtn: false,
          freeEpisodesCount: 999,           // VIP用户无限免费
          unlockEpisodesCountUsingAd: 0,    // 不需要广告解锁
          enableInfiniteScroll: true,       // 自动轮播下一部短剧
        ),
      ),
    );
  }
}

场景3:活动推广页

指定置顶推荐的短剧,用于活动推广:

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('热门推荐')),
      body: DramaHomeNativeView(
        config: const DramaHomeConfig(
          showBackBtn: false,
          topDramaIds: [
            '1008',  // 推广短剧1
            '1009',  // 推广短剧2
            '1010',  // 推广短剧3
          ],
          freeEpisodesCount: 3,
          showChangeBtn: false,  // Android: 隐藏"换一换"按钮
        ),
      ),
    );
  }
}

场景4:自定义标题栏(Android)

使用自定义标题栏,调整聚合页的顶部偏移:

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          // 自定义标题栏(高度 56dp)
          Container(
            height: 56,
            color: Colors.blue,
            child: const SafeArea(
              bottom: false,
              child: Center(
                child: Text(
                  '短剧推荐',
                  style: TextStyle(color: Colors.white, fontSize: 18),
                ),
              ),
            ),
          ),
          // 短剧聚合页
          Expanded(
            child: DramaHomeNativeView(
              config: const DramaHomeConfig(
                showBackBtn: false,
                showPageTitle: false,  // Android: 隐藏SDK默认标题栏
                topOffset: 56,         // Android: 顶部偏移56dp
                freeEpisodesCount: 3,
              ),
            ),
          ),
        ],
      ),
    );
  }
}

🛠️ 故障排查

问题1:聚合页无法创建或显示空白

可能原因

  1. 未完成插件初始化,确保调用了 PangrowthContent.start()
  2. 穿山甲SDK配置不正确,检查 appIdappName 是否正确
  3. 单实例约束:已存在一个聚合页实例未销毁

解决方法

// 1. 确保在使用聚合页前完成初始化
await PangrowthContent.start(
  startDrama: true,  // 启动短剧功能
);

// 2. 验证配置是否正确
DramaHomeNativeView(
  config: const DramaHomeConfig(
    freeEpisodesCount: 3,  // 提供基础配置
  ),
);

// 3. 监听错误事件
DramaHomeListener(
  onHomeError: (error) {
    debugPrint('❌ 聚合页错误: $error');
    // 根据错误类型进行处理
    if (error.contains('SDK_NOT_READY')) {
      // 提示用户 SDK 未初始化
    } else if (error.contains('INSTANCE_EXISTS')) {
      // 提示用户已存在聚合页实例
    }
  },
)

问题2:单实例冲突

原因:同一时刻只能存在一个聚合页实例,尝试创建新实例时旧实例未销毁

解决方法

// 方式1: 使用 Navigator 管理页面栈,确保页面退出时自动销毁
Navigator.push(
  context,
  MaterialPageRoute(
    builder: (context) => const DramaHomePage(),
  ),
);

// 方式2: 在 TabBar 中使用时,确保只有一个 Tab 包含聚合页
TabBarView(
  children: [
    const Center(child: Text('其他页面')),
    DramaHomeNativeView(  // 只在一个 Tab 中使用
      config: const DramaHomeConfig(freeEpisodesCount: 3),
    ),
    const Center(child: Text('其他页面')),
  ],
)

// 方式3: API 方式需要手动销毁
final launcher = DramaHomeLauncher();
await launcher.openDramaHome();
// ... 使用完毕后
await launcher.dispose();  // 必须手动销毁

问题3:聚合页内存泄漏

原因:未正确销毁聚合页实例

解决方法

@override
void dispose() {
  // 方式1: NativeView 会自动销毁,无需手动处理
  // (除非缓存了 viewId 并手动管理生命周期)

  // 方式2: API 方式必须手动销毁
  if (_widgetId != null) {
    PangrowthContent.destroyDramaHome(_widgetId!);
  }

  super.dispose();
}

问题4:播放详情页UI显示异常

原因:配置参数设置不正确

解决方法

// 检查各项配置参数是否符合预期
DramaHomeNativeView(
  config: const DramaHomeConfig(
    freeEpisodesCount: 3,
    // 确认UI控制参数设置正确
    hideTopInfo: false,        // false=显示,true=隐藏
    hideBottomInfo: false,
    hideBack: false,           // 根据需求设置
    hideLikeIcon: false,
    hideFavorIcon: false,
  ),
)

问题5:置顶短剧不生效

原因:置顶短剧ID格式错误或短剧不存在

解决方法

// 方式1: 单个置顶短剧(使用 int 类型)
DramaHomeNativeView(
  config: const DramaHomeConfig(
    topDramaId: 1008,  // 确保短剧ID存在且格式正确
  ),
)

// 方式2: 多个置顶短剧(使用 String 数组)
DramaHomeNativeView(
  config: const DramaHomeConfig(
    topDramaIds: ['1008', '1009', '1010'],  // String 数组格式
  ),
)

// 注意: topDramaIds 优先级高于 topDramaId

调试建议

  1. 开启日志输出:在代码中使用 debugPrint 查看详细信息
  2. 确认SDK版本:确保SDK版本和文档版本匹配
  3. 检查网络连接:聚合页需要网络加载内容
  4. 验证配置参数:检查 PangrowthContent.initialize()start() 的配置
  5. 测试基础功能:先使用最简配置进行测试,再逐步添加自定义配置

🌍 平台差异说明

参数映射

以下通用参数在 Flutter 层已自动处理平台差异,可直接使用:

  • showBackBtn (双端通用)

    • Android:直接控制返回按钮显示
    • iOS:自动映射到 isShowNavigationItemBackButton 参数
  • enableInfiniteScroll (双端通用)

    • Android:直接使用
    • iOS:内部会取反处理(iOS SDK 语义相反)

iOS 导航栏

iOS 平台默认展示系统导航栏,如需使用 Flutter 自定义标题栏,可同时设置:

DramaHomeNativeView(
  config: const DramaHomeConfig(
    showPageTitle: false,                      // Android: 隐藏标题栏
    isShowNavigationItemTitle: false,          // iOS: 隐藏导航栏标题
    isShowNavigationItemBackButton: false,     // iOS: 隐藏导航栏返回按钮
  ),
)

Android 顶部偏移

topOffset 参数仅在 Android 平台有效,适合配合自定义 AppBar 使用:

DramaHomeNativeView(
  config: const DramaHomeConfig(
    showPageTitle: false,  // Android: 隐藏SDK默认标题栏
    topOffset: 56,         // Android: 顶部偏移56dp(自定义标题栏高度)
  ),
)

单位为 dp,iOS 会忽略此参数。

平台支持总结

特性AndroidiOS说明
短剧聚合页✅ 完整支持✅ 完整支持双端均支持
NativeView组件✅ 可用✅ 可用双端均通过 PlatformView 实现
API方式✅ 可用✅ 可用双端均支持 create/show/destroy API
showChangeBtn✅ 支持-仅Android,iOS无此UI元素
showPageTitle✅ 支持-仅Android,iOS使用系统导航栏
topOffset✅ 支持-仅Android,iOS无此参数
iOS导航栏配置-✅ 支持仅iOS,通过 isShowNavigationItem* 参数控制

📚 相关文档


💡 提示: 短剧聚合页功能需要有效的内容源和网络连接。建议在正式使用前先进行测试,确保内容加载正常。

需要进一步协助?

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