dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:shirne_dialog/shirne_dialog.dart';
import '../utils/api.dart';
import '../utils/logger.dart';
import '../widgets/tags/empty.dart';
import '../application.dart';
import 'home/home.dart' show HomePage;
import 'channel/index.dart' as channel;
import 'member/index.dart' as member;
class IndexPage extends StatefulWidget {
const IndexPage({Key? key}) : super(key: key);
@override
State<IndexPage> createState() => _IndexPageState();
}
class _IndexPageState extends State<IndexPage> {
int _lastTapBack = 0;
int _selectedIndex = 0;
late final PageController _controller =
PageController(initialPage: _selectedIndex);
bool initialized = false;
bool isError = false;
bool isReload = false;
@override
initState() {
super.initState();
_init();
}
@override
dispose() {
_controller.dispose();
super.dispose();
}
_init() async {
try {
await app.login.restoreToken();
await app.getSiteinfo();
setState(() {
initialized = true;
});
} on ServerException catch (_) {
setState(() {
isError = true;
});
}
}
void _onItemTapped(index) {
if (index == 2) {
int curPage = _controller.page?.round() ?? 0;
setState(() {
_selectedIndex = curPage > 1 ? (curPage + 1) : curPage;
});
app.route('/feedback');
} else {
index = index > 1 ? (index - 1) : index;
setState(() {
_selectedIndex = index;
_controller.animateToPage(
_selectedIndex,
duration: const Duration(milliseconds: 300),
curve: Curves.easeIn,
);
});
}
}
@override
Widget build(BuildContext context) {
if (!initialized) {
return AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
child: isError ? renderError() : renderLoading(),
);
}
return WillPopScope(
onWillPop: () {
int now = DateTime.now().millisecondsSinceEpoch;
logger.info('onWillPop $_lastTapBack $now');
if (now - _lastTapBack > 800) {
_lastTapBack = now;
MyDialog.toast(
'再次点击返回退出应用',
duration: const Duration(seconds: 1),
style: MyDialog.theme.toastStyle?.bottom(),
);
return Future.value(false);
} else {
return Future.value(true);
}
},
child: Scaffold(
body: PageView(
controller: _controller,
onPageChanged: (index) {
setState(() {
_selectedIndex = index > 1 ? (index + 1) : index;
});
},
children: const [
HomePage(),
channel.IndexPage(channel: 'blog'),
channel.IndexPage(channel: 'works'),
member.IndexPage(),
],
),
bottomNavigationBar: BottomAppBar(
shape: const CircularNotchedRectangle(),
elevation: 16,
child: BottomNavigationBar(
backgroundColor: Colors.transparent,
elevation: 0,
items: const [
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.house),
activeIcon: Icon(CupertinoIcons.house_fill),
label: '首页',
),
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.settings_solid),
activeIcon: Icon(CupertinoIcons.settings_solid),
label: '自动',
),
BottomNavigationBarItem(icon: SizedBox(), label: ''),
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.hand_raised),
activeIcon: Icon(CupertinoIcons.hand_raised_fill),
label: '手动',
),
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.person_alt_circle),
activeIcon: Icon(CupertinoIcons.person_alt_circle_fill),
label: '会员',
),
],
currentIndex: _selectedIndex,
//selectedItemColor: Colors.blue,
onTap: _onItemTapped,
type: BottomNavigationBarType.fixed,
//unselectedItemColor: Colors.grey,
selectedFontSize: 12,
unselectedFontSize: 12,
showUnselectedLabels: true,
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
app.route('/feedback');
},
heroTag: 'feedback-page',
child: const Icon(CupertinoIcons.paperplane),
),
floatingActionButtonLocation:
FloatingActionButtonLocation.miniCenterDocked,
),
);
}
Widget renderError() {
return InkWell(
onTap: () {
setState(() {
initialized = false;
isError = false;
isReload = true;
});
_init();
},
child: const Center(
child: TagEmpty(
'网络连接错误,点击重试',
icon: Icons.wifi_off,
),
),
);
}
Widget renderLoading() {
return Container(
color: Colors.white,
child: const Center(
child: CircularProgressIndicator(),
),
);
}
}
这段代码是一个Flutter应用的主页面框架实现,以下是主要功能解释:
1. 基本结构
IndexPage
是一个 StatefulWidget
,实现了带底部导航栏的主界面。
2. 导航系统
- 底部有5个导航选项:首页、自动、中间浮动按钮、手动和会员。
- 导航栏使用了自定义图标,比如设置图标表示"自动",手势图标表示"手动"。
3. 页面控制
- 使用
PageView
控制不同页面的切换,可以通过滑动或点击底部导航栏切换。 - 实现了平滑的页面过渡动画(持续300毫秒的 ease-in 动画)。
4. 初始化流程
- 应用启动时会尝试恢复用户登录状态并获取站点信息。
- 显示加载画面或错误画面,直到初始化完成。
5. 返回键处理
- 实现了双击返回退出功能,防止用户意外退出应用。
- 第一次点击返回会显示提示,800毫秒内再次点击才会退出。
6. UI 特点
- 底部导航栏采用透明背景。
- 中间有一个浮动按钮,指向反馈页面。
- 使用了
CircularNotchedRectangle
形状,为浮动按钮留出位置。
7. 错误处理
- 提供了友好的错误界面,允许用户点击重试。
- 网络异常时会显示错误信息。
整体而言,这是一个标准的 Flutter 应用主框架,采用了底部导航 + 页面切换的常见模式,实现了应用的主要导航功能,包括首页、自动模式、手动模式和会员中心等主要功能区域的切换。