普通视图
Flutter 复用艺术:Mixin 与 Abstract 的架构哲学与线性化解密
零一开源|前沿技术周刊 #12
「内力探查术」:用 Instruments 勘破 SwiftUI 卡顿迷局
Swift Concurrency:彻底告别“线程思维”,拥抱 Task 的世界
深入理解 Swift 中的 async/await:告别回调地狱,拥抱结构化并发
深入理解 SwiftUI 的 ViewBuilder:从隐式语法到自定义容器
在 async/throwing 场景下优雅地使用 Swift 的 defer 关键字
我差点失去了巴顿(我的狗狗) | 肘子的 Swift 周报 #098
当Swift Codable遇到缺失字段:优雅解决数据解码难题
RunLoop 实现原理
用 SwiftUI 打造一个 iOS「设置」界面
三年期已满,你的产品不再更新将于90天后下架。
架构整洁之道 —— Clean Architecture
iOS26适配指南之UIButton
WWDC 2025 Build a SwiftUI app with the new design
Build a SwiftUI app with the new design
WWDC 2025推出了liquid glass这种重磅级的新设计,怎样在swiftUI开发中用上这些新特性,给你的用户带来耳目一新的视觉盛宴呢,今天我们来聊一下
总结起来就是这个 session 展示了如何用 Liquid Glass 与新 SwiftUI API 重塑 App 结构(导航、标签页、工具栏、搜索、控件),在 iOS 26 和 macOS Tahoe 上实现更轻盈、动态、统一的用户体验。
具体来讲就是以Liquid Glass为中心,在不同场景包括NavigationSplitView, Inspector, TabView, Sheets, Toolbar和不同控件按钮、滑块、菜单的结合,呈现新的设计效果
1. 新设计的核心理念
- Liquid Glass 是 iOS 26 和 macOS Tahoe 的全新自适应材质,用于 控制和导航元素。
- 它结合玻璃的光学特性和液体的流动感,创造出轻盈、动态的视觉效果,帮助内容成为界面的主角。
- 在交互时(比如切换、滑动、点击),控件会“活起来”,提供流体化的反馈。
2. 应用结构更新
-
NavigationSplitView:侧边栏采用 Liquid Glass,浮动在内容上,配合新的
backgroundExtensionEffect
可避免图片被裁剪。 -
Inspector:对比侧边栏,采用更细腻的分层效果,与所选内容建立视觉联系。
-
TabView:
- 新的 Tab Bar 可 悬浮在内容之上,并通过
tabBarMinimizeBehavior
设置滚动时的折叠/展开行为。 -
tabViewBottomAccessory
允许在 Tab Bar 下方附加额外控件(如播放控件)。
- 新的 Tab Bar 可 悬浮在内容之上,并通过
-
Sheets:
- 默认带有 Liquid Glass 背景。
- 支持 部分高度,边缘自动与屏幕圆角对齐。
- 转换到全屏时会逐渐过渡为不透明背景。
- 新的 导航缩放过渡 让 Sheet/对话框看起来从按钮“流出”。
3. 工具栏 (Toolbars)
- 工具栏表面为 Liquid Glass,自适应底层内容。
- 项目可自动分组,也可用
ToolbarSpacer
控制分组间距。 -
badge
修饰符为工具栏按钮提供消息提醒。 -
sharedBackgroundVisibility
可以将项目单独分组,避免背景混淆。 - 图标更偏向单色,减少干扰;但仍可通过
tint
传达语义。 -
滚动边缘效果 (scrollEdgeEffect) 自动处理内容与浮动控件之间的可读性,可通过
scrollEdgeEffectStyle
调整。
4. 搜索体验
-
提供 两大模式:
- 工具栏内搜索:iPhone 显示在底部,iPad/Mac 显示在右上角。
-
独立的搜索页面:可在 TabView 中定义
search
角色。
-
新的
searchToolbarBehavior
API 可精细控制搜索的折叠/展开方式。 -
搜索框本身置于 Liquid Glass 表面,激活时与内容保持自然过渡。
5. 标准控件的更新
-
按钮 (Buttons) :
- 默认采用 胶囊形状,与系统圆角保持一致。
- 新增 extra-large 尺寸,强调主操作。
- 新的
glass
与glassProminent
样式将 Liquid Glass 引入按钮。
-
滑块 (Sliders) :
- 支持 刻度 (tick marks) ,可自动或手动配置。
-
neutralValue
参数允许定义中点值(如播放速度调节)。
-
菜单 (Menus) :
- 图标统一在左侧,iOS 与 macOS 行为一致。
-
一致性特性:
- 新的 corner concentricity 概念:控件自动与容器(如 Sheet)保持圆角同心。
6. 自定义 Liquid Glass
- 使用
glassEffect
修饰符为自定义控件加上 Liquid Glass。 - 通过
GlassEffectContainer
管理多个玻璃元素,确保光影一致。 - 使用
glassEffectID
与namespace
配合,可以实现 流体化的玻璃转场(比如徽章展开/收缩)。 -
interactive
修饰符让自定义玻璃控件具备触摸反馈(缩放、弹性、闪光)。 - 支持 tint 高亮关键控件,但应避免过度装饰。
7. 最佳实践与采纳策略
- 使用 Xcode 26 SDK 构建,许多 UI 更新自动获得。
- 检查 App 结构,移除不必要的背景,避免与系统滚动边缘效果冲突。
- 优先使用系统控件 来获得一致性和自动适配。
- 在必要时才引入自定义 Liquid Glass 元素,让 App 的独特之处得到凸显。
SwiftUI 劝退实录:AI 都无能为力,你敢用吗?
Flutter 实现类似抖音/TikTok 的竖向滑动短视频播放器
近年来,短视频平台(如抖音、TikTok)已经成为主流的内容消费方式。本文将分享一个用 Flutter 实现的 竖向滑动短视频播放器,支持自动播放、滑动切换、视频信息展示等核心功能。
功能概述
- 竖向 PageView 滑动切换视频
- 自动播放当前视频,暂停非当前视频
- 视频信息展示(作者头像、昵称、标题、音乐信息等)
- 视频互动按钮(点赞、评论、转发等)
- 黑色沉浸式 UI
效果类似 TikTok/抖音。


项目结构
lib/
app/ # 应用入口
packages/app_ui/ # 全局主题样式
reels/ # 短视频模块
bloc/ # 状态管理(Bloc)
model/ # 数据模型与仓库
reel/ # 单条视频展示
view/ # 视频列表(PageView)
1. 应用入口与主题
入口 main.dart
:
import 'package:reel_views/app/app.dart';
import 'package:reel_views/bootstrap.dart';
void main() {
bootstrap(() => const App());
}
应用 App
中使用 Bloc 提供 PostsRepository
和 ReelBloc
,并应用自定义暗色主题 AppDarkTheme
:
class App extends StatelessWidget {
const App({super.key});
@override
Widget build(BuildContext context) {
return RepositoryProvider(
create: (context) => PostsRepository(),
child: MaterialApp(
theme: const AppDarkTheme().theme,
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
home: BlocProvider(
create: (context) => ReelBloc(
postsRepository: context.read<PostsRepository>()
),
child: const ReelsView(),
),
),
);
}
}
2. 数据模型与仓库
短视频数据来自 PostsRepository
:
class PostsRepository {
static final recommendedReels = [
PostReelBlock(
author: PostAuthor.randomConfirmed(),
id: "1",
caption: "送你一朵小红花",
media: 'assets/video/Butterfly-209.mp4'
),
...
];
}
作者信息用 PostAuthor
封装,并支持随机生成测试数据:
class PostAuthor {
const PostAuthor({
required this.id,
required this.avatarUrl,
required this.username,
this.isConfirmed = false
});
factory PostAuthor.randomConfirmed() {
final randomUser = _confirmedUsers[Random().nextInt(_confirmedUsers.length)];
return PostAuthor(
id: randomUser.id,
username: randomUser.username!,
avatarUrl: randomUser.avatarUrl!,
isConfirmed: true,
);
}
}
3. 状态管理(Bloc)
ReelBloc
负责视频数据加载:
class ReelBloc extends Bloc<ReelEvent, ReelState> {
ReelBloc({ required PostsRepository postsRepository })
: _postsRepository = postsRepository,
super(const ReelState.initial()) {
on<ReelRecommendedPostsPageRequested>(_onReelRecommendedPostsPageRequested);
}
Future<void> _onReelRecommendedPostsPageRequested(
ReelRecommendedPostsPageRequested event,
Emitter<ReelState> emit,
) async {
emit(state.loading());
final recommendedBlocks = [...PostsRepository.recommendedReels..shuffle()];
emit(state.populated(blocks: recommendedBlocks));
}
}
4. 视频列表页面(竖向 PageView)
ReelsView
使用 PageView.builder
实现竖向滑动:
class _ReelsViewState extends State<ReelsView> {
late PageController _pageController;
late ValueNotifier<int> _currentIndex;
@override
void initState() {
super.initState();
_pageController = PageController(keepPage: false);
_currentIndex = ValueNotifier(0);
context.read<ReelBloc>().add(const ReelRecommendedPostsPageRequested());
}
@override
Widget build(BuildContext context) {
return BlocBuilder<ReelBloc, ReelState>(
builder: (context, state) {
final blocks = state.blocks;
return PageView.builder(
controller: _pageController,
scrollDirection: Axis.vertical,
onPageChanged: (index) => _currentIndex.value = index,
itemCount: blocks.length,
itemBuilder: (context, index) {
final block = blocks[index];
final isCurrent = index == _currentIndex.value;
return Reel(
key: ValueKey(block.id),
play: isCurrent,
block: block,
);
},
);
},
);
}
}
5. 单条视频展示
Reel
组件负责渲染单条视频及 UI 叠层:
class Reel extends StatefulWidget {
const Reel({ required this.block, required this.play, super.key });
final PostReelBlock block;
final bool play;
@override
State<Reel> createState() => _ReelState();
}
class _ReelState extends State<Reel> {
late VideoPlayerController _videoController;
@override
void initState() {
super.initState();
_videoController = VideoPlayerController.asset(widget.block.media);
}
@override
Widget build(BuildContext context) {
return InlineVideo(
shouldPlay: widget.play,
videoPlayerController: _videoController,
stackedWidget: Stack(
children: [
VerticalButtons(widget.block),
// 这里省略底部作者和标题信息布局
],
),
);
}
}
6. 视频播放器封装
InlineVideo
对 video_player
进行了封装,实现自动播放/暂停:
class InlineVideo extends StatefulWidget {
const InlineVideo({
required this.shouldPlay,
required this.stackedWidget,
required this.videoPlayerController,
super.key
});
@override
State<InlineVideo> createState() => _InlineVideoState();
}
class _InlineVideoState extends State<InlineVideo> {
late VideoPlayerController _controller;
@override
void initState() {
super.initState();
_controller = widget.videoPlayerController;
_controller.initialize().then((_) {
if (widget.shouldPlay) _controller..play()..setLooping(true);
});
}
@override
void didUpdateWidget(covariant InlineVideo oldWidget) {
if (oldWidget.shouldPlay != widget.shouldPlay) {
widget.shouldPlay ? _controller.play() : _controller.pause();
}
super.didUpdateWidget(oldWidget);
}
@override
Widget build(BuildContext context) {
return Stack(
children: [
AspectRatio(
aspectRatio: _controller.value.aspectRatio,
child: VideoPlayer(_controller),
),
widget.stackedWidget
],
);
}
}
7. 视频交互按钮
右侧互动按钮由 VerticalButtons
实现:
class VerticalButtons extends StatelessWidget {
const VerticalButtons(this.block, {super.key});
final PostReelBlock block;
@override
Widget build(BuildContext context) {
return Align(
alignment: Alignment.bottomRight,
child: Column(
children: [
const VerticalGroup(icon: Icons.favorite_outline, statisticCount: 30),
VerticalGroup(
statisticCount: 2,
child: SvgPicture.asset('assets/icons/chat_circle.svg', width: 30),
),
const VerticalGroup(icon: Icons.more_vert_sharp, withStatistic: false),
CircleAvatar(backgroundImage: NetworkImage(block.author.avatarUrl)),
],
),
);
}
}
总结与优化方向
本文用 Flutter + Bloc + video_player 实现了一个竖向短视频播放器,具备 TikTok 类似的交互体验。
未来可以优化的方向:
- 视频缓存与预加载:减少切换时的延迟
- 网络视频支持:支持从 API 拉取视频
- 手势交互:双击点赞、长按暂停
- 性能优化:更细粒度的资源释放与控制
你可以直接在此基础上添加更多功能,打造属于自己的短视频应用。