引言
在移动应用开发中,用户界面的布局设计是提升用户体验的关键要素。Flutter作为一个强大的UI框架,提供了多种布局组件,使开发者能够灵活地构建复杂的用户界面模块。无论是线性布局(如Column和Row)还是非线性布局(如Stack和Wrap),Flutter的布局系统都允许开发者以简洁的方式组织和组合界面元素。
小demo
登录页面
动画依赖
shake_animation_widget: ^3.0.4 import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:shake_animation_widget/shake_animation_widget.dart';
import 'dart:async'; import 'package:flutter/material.dart'; class test01 extends StatefulWidget { const test01({Key? key}) : super(key: key); @override State<test01> createState() => _test01State(); } class _test01State extends State<test01> { @override Widget build(BuildContext context) { return Scaffold( //当前显示页面的背景 backgroundColor: Colors.white, body: Container( //填充父布局空间 width: double.infinity, height: double.infinity, //一个层叠布局 child: Stack( //内容居中 alignment: Alignment.center, children: [ LoginInputLayout(), ], ), ), ); } } class LoginInputLayout extends StatefulWidget { @override _TestPageState createState() => _TestPageState(); } class _TestPageState extends State<LoginInputLayout> { //用户名输入框的焦点控制 final FocusNode _userNameFocusNode = new FocusNode(); final FocusNode _passwordFocusNode = new FocusNode(); //文本输入框控制器 final TextEditingController _userNameController = new TextEditingController(); final TextEditingController _passwordController = new TextEditingController(); //抖动动画控制器 //Stream 更新错误方案操作控制器 StreamController<String> _userNameStream = new StreamController(); StreamController<String> _userPasswordStream = new StreamController(); ///代码清单 3-23 输入层build方法 ///lib/code/code3/example_311_login_page.dart @override Widget build(BuildContext context) { //手势识别点击空白隐藏键盘 return GestureDetector( onTap: () { hindKeyBoarder(); }, child: Scaffold( //键盘弹出不移动布局 // resizeToAvoidBottomPadding: false, //背景透明 backgroundColor: Colors.transparent, //登录页面的主体 body: Container( width: double.infinity, height: double.infinity, child: buildLoginWidget(), ), ), ); } ///代码清单 3-24 ///lib/code/code3/example_311_login_page.dart Widget buildLoginWidget() { return Column( mainAxisSize: MainAxisSize.max, children: [ Container( margin: const EdgeInsets.only( left: 30.0, right: 30.0, ), //内边距 padding: const EdgeInsets.all(16), //圆角边框 decoration: BoxDecoration( //透明的白色 color: Colors.white.withOpacity(0.8), //四个圆角 borderRadius: const BorderRadius.all(Radius.circular(12))), //线性布局 child: buildColumn(), ), ], ); } ///代码清单 3-25 ///lib/code/code3/example_311_login_page.dart Column buildColumn() { return Column( //包裹 mainAxisSize: MainAxisSize.min, children: [ //用户名输入框 buildUserNameWidget(), const SizedBox( height: 20, ), //用户密码输入框 buildUserPasswordWidget(), const SizedBox( height: 40, ), //登录按钮 Container( width: double.infinity, height: 40, child: ElevatedButton( child: Text("登录"), onPressed: () { checkLoginFunction(); }, ), ) ], ); } ///代码清单 3-26 抖动用户名输入框构建 ///lib/code/code3/example_311_login_page.dart Widget buildUserNameWidget() { return TextField( focusNode: _userNameFocusNode, onTap: () { setState(() { if (checkUserName()) { _userNameFocusNode.unfocus(); FocusScope.of(context).requestFocus(_passwordFocusNode); } else { FocusScope.of(context).requestFocus(_userNameFocusNode); } }); }, decoration: InputDecoration( labelText: "用户名", border: OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(10)) ) ), onSubmitted: (String value) { if (checkUserPassword()) { loginFunction(); } else { FocusScope.of(context).requestFocus(_passwordFocusNode); } }, ); } ///代码清单 3-27 抖动密码输入框构建 ///lib/code/code3/example_311_login_page.dart Widget buildUserPasswordWidget() { return TextField( focusNode: _passwordFocusNode, controller: _passwordController, onTap: (){ setState(() { }); }, decoration: InputDecoration( labelText: "密码", border: OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(10)) ) ), onSubmitted: (String value) { if (checkUserPassword()) { loginFunction(); } else { FocusScope.of(context).requestFocus(_passwordFocusNode); } }, ); } ///代码清单 3-28 隐藏键盘操作 ///lib/code/code3/example_311_login_page.dart void hindKeyBoarder() { //输入框失去焦点 _userNameFocusNode.unfocus(); _passwordFocusNode.unfocus(); } bool checkUserName() { //获取输入框中的输入文本 String userName = _userNameController.text; print(userName); if (userName.length == 0) { //Stream 事件流更新提示文案 _userNameStream.add("请输入用户名"); //抖动动画开启 return false; } else { //清除错误提示 _userNameStream.add(''); return true; } } ///代码清单 3-30 校验输入密码操作 ///lib/code/code3/example_311_login_page.dart bool checkUserPassword() { String userPassrowe = _passwordController.text; if (userPassrowe.length < 6) { _userPasswordStream.add("请输入标准密码"); return false; } else { _userPasswordStream.add(''); return true; } } void checkLoginFunction() { //隐藏键盘 hindKeyBoarder(); //校验用户输入的用户名 checkUserName(); //校验用户输入的用户密码 checkUserPassword(); //登录功能 loginFunction(); } void loginFunction() {} }
shake_animation_widget: ^3.0.4
按钮加一
import 'package:flutter/material.dart'; class Example01 extends StatefulWidget{ const Example01({super.key}); @override State<StatefulWidget> createState() { return _ExampleState(); } } class _ExampleState extends State<Example01>{ int _count=0; @override Widget build(BuildContext context) { // Scaffold 用来 搭建主体结构 return Scaffold( appBar: AppBar( title: const Text('hello,world'),), body: Center( child: Text('$_count'), ), floatingActionButton: FloatingActionButton( child: const Icon(Icons.add),onPressed: (){ ++_count; }, ), ); } } void main(){ runApp(MaterialApp(home: Example01(),)); }
列表加一
import 'package:flutter/material.dart'; class Myapp extends StatefulWidget { const Myapp({Key? key}) : super(key: key); @override State<Myapp> createState() => _MyappState(); } class _MyappState extends State<Myapp> { List<String> _list = ['i am the first','i am the second']; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("hello,flutter"), ), body: ListView( children: _list.map((v) { return ListTile( title: Text(v), ); }).toList(), ), floatingActionButton: FloatingActionButton( onPressed: () { // 改变数据 需要 加 setState setState(() { // _list.add(value); _list.add('i am a new list'); }); }, child: Icon(Icons.add), ), ); } } void main() { runApp(MaterialApp( home: Myapp(), )); }
搜索页面
import 'package:flutter/material.dart'; class Myapp extends StatelessWidget { const Myapp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( title: 'hello flutter', theme: ThemeData( primarySwatch: Colors.blue, ), home: Scaffold( appBar: AppBar( title: Text("flutter app"), ), body: LayoutDemo(), ), ); } } class LayoutDemo extends StatelessWidget { const LayoutDemo({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return ListView( padding: EdgeInsets.all(10), children: [ Row( children: [ Text( 'hot bar', style: Theme.of(context).textTheme.headlineSmall, ), ], ), Divider(), // 线 Wrap( spacing: 10, // x axis 水平间距 runSpacing: 10, // y axis 垂直 children: [ Button( "phone", onPressed: () {}, ), Button( "computer", onPressed: () {}, ), Button( "girl clothes", onPressed: () {}, ), Button( "boy clothes", onPressed: () {}, ), Button( "literature", onPressed: () {}, ), ], ), SizedBox( height: 20, ), Row( children: [ Text( 'history', style: Theme.of(context).textTheme.headlineSmall, ), ], ), Divider(), // 线 Column( children: [ ListTile( title: Text("computer"), ), Divider(), ListTile( title: Text("literature"), ), Divider(), ListTile( title: Text("boy clothes"), ), Divider(), ], ), SizedBox(height: 40,), Padding(padding: EdgeInsets.all(40), child: OutlinedButton.icon( style: ButtonStyle( foregroundColor: MaterialStateProperty.all(Colors.black45), ), onPressed: (){}, icon:Icon(Icons.delete), label: Text("clear history")),) ], ); } } // 自定义按钮 class Button extends StatelessWidget { final String text; final void Function()? onPressed; Button(this.text, {Key? key, required this.onPressed}) : super(key: key); @override Widget build(BuildContext context) { return ElevatedButton( style: ButtonStyle( backgroundColor: MaterialStateProperty.all(Color.fromRGBO(241, 255, 244, 244)), foregroundColor: MaterialStateProperty.all(Colors.black45)), onPressed: onPressed, child: Text(text)); } } void main() { runApp(const Myapp()); }
轮播图
先创建 出口
void main() { runApp(MaterialApp( debugShowCheckedModeBanner: false, title: "轮播图实例", theme: ThemeData(primaryColor: Colors.grey), home: MyApp(), )); }
用来接收的组件
import 'package:flutter/material.dart'; import 'package:flutter03/banner/bannerPage.dart'; class MyApp extends StatefulWidget{ const MyApp({Key? key}) : super(key: key); @override State<MyApp> createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { List<String> list = []; @override void initState() { super.initState(); list = [ "images/noodle.jpg", "images/laying.jpg", "images/umbrella.jpg", ]; } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("轮播图实例"), centerTitle: true, ), body: ListView( children: [ // 调用 bannerpage 组件 bannerPage(list: list) ], ), ); } }
图片组件
import 'package:flutter/material.dart'; class ImagePage extends StatelessWidget { final double width; final double height; final String src; ImagePage({Key? key,this.width=double.infinity,this.height=200,required this.src}) : super(key: key); @override Widget build(BuildContext context) { return SizedBox( width: width, height: height, child: Image.asset(src,fit: BoxFit.cover,), ); } }
轮播图组件
import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter03/components/imgPage.dart'; class bannerPage extends StatefulWidget { final double width; final double height; final List<String> list; const bannerPage( {Key? key, this.width = 400, this.height = 300, required this.list}) : super(key: key); @override State<bannerPage> createState() => _bannerPageState(); } class _bannerPageState extends State<bannerPage> { List<Widget> ImgList = []; int _imgIndex = 0; late PageController _pageController; late Timer _timer; @override void initState() { // TODO: implement initState super.initState(); _pageController = new PageController(initialPage: 0); // duration 过 多少秒 重复 一次 _timer = Timer.periodic(Duration(milliseconds: 4000), (timer) { // 执行 控制器 _pageController.animateToPage(_imgIndex + 1 % ImgList.length, // 执行的 时间 长度 duration: Duration(milliseconds: 500), curve: Curves.linear); }); for (var i = 0; i < widget.list.length; i++) { // list 是 自己定义的 数组 长度 ImgList.add(ImagePage( src: widget.list[i], width: widget.width, height: widget.height, )); } } @override void dispose() { // TODO: implement dispose super.dispose(); _pageController.dispose(); } @override Widget build(BuildContext context) { return Stack( children: [ SizedBox( height: 500, width: double.infinity, child: PageView.builder( itemCount: 1000, controller: _pageController, onPageChanged: (index) { setState(() { _imgIndex = index % ImgList.length; }); }, itemBuilder: (BuildContext context, int index) { return ImgList[index % ImgList.length]; }), ), Positioned( bottom: 0, left: MediaQuery.of(context).size.width / 2, child: Row( children: List.generate(3, (index) { return Container( width: 15, height: 15, decoration: BoxDecoration( color: _imgIndex == index ? Colors.blue : Colors.grey, shape: BoxShape.circle), ); }).toList(), )) ], ); } }
中英文切换
先导包
get: ^4.3.8
创建使用
return GetMaterialApp( debugShowCheckedModeBanner: false, title: "title", // 国际化 配置 化 文件 translations: Messages(), // 默认 语言 locale: Locale('zh', 'CN'), // 配置错误 使用的语言 fallbackLocale: Locale('zh', 'CN'), home: MyTest(), // );
配置文件
import 'package:get/get.dart'; class Messages extends Translations{ @override Map<String,Map<String,String>> get keys =>{ 'zh_CN':{ 'hello':"你好,世界" }, 'en_US':{ 'hello':"hello,world" } }; }
控制器
import 'dart:ui'; import 'package:get/get.dart'; class MessagesController extends GetxController{ void changeLanguage(String languageCode,String countryCode){ var locale=Locale(languageCode,countryCode); Get.updateLocale(locale); } }
页面广告倒计时
late Timer _timer; double progress=1000; double totalProgress=6000; double borderWidth=1.0; int time=5; @override void initState() { // TODO: implement initState super.initState(); _timer= Timer.periodic(Duration(milliseconds: 1000), (timer) { progress+=1000; time--; print(time); if(progress>=totalProgress){ _timer.cancel(); goHome(); } }); } @override void dispose() { // TODO: implement dispose super.dispose(); _timer.cancel(); }
body: Column( children: [ Container( child: Text( '倒计时: $time', style: TextStyle(fontSize: 22), ), ), ], )
goHome(){ return Navigator.push( context as BuildContext,MaterialPageRoute(builder: (context){ return Test03(); })); }
import 'dart:async'; import 'package:flutter/material.dart'; class CountdownPage extends StatefulWidget { @override _CountdownPageState createState() => _CountdownPageState(); } class _CountdownPageState extends State<CountdownPage> { late Timer _timer; int _second = 10; @override void initState() { super.initState(); startTimer(); } void startTimer() { _timer = Timer.periodic(Duration(milliseconds: 1000), (timer) { setState(() { if (_second < 1) { _timer.cancel(); } else { _second--; } }); }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Countdown Page'), ), body: Center( child: Text( "$_second", style: TextStyle( fontSize: 48, fontWeight: FontWeight.bold, ), ), ), ); } @override void dispose() { _timer.cancel(); super.dispose(); } } void main() { runApp(MaterialApp( home: CountdownPage(), )); }
顶部的导航条
先写一个 出口
import 'dart:math'; import 'package:flutter/material.dart'; import 'MyApp.dart'; void main() { runApp(MaterialApp( debugShowCheckedModeBanner: false, title: "轮播图实例", theme: ThemeData(primaryColor: Colors.grey), home: MyApp(), )); }
写一个 searchPage
import 'package:flutter/material.dart'; class SearchAppBar extends StatefulWidget { // 带 的 属性 hint label String hintLabel; SearchAppBar({Key? key, required this.hintLabel}) : super(key: key); @override State<SearchAppBar> createState() => _SearchAppBarState(); } class _SearchAppBarState extends State<SearchAppBar> { // focus late FocusNode _focusNode; // 组件 的 显示 控制 bool _offstAge = false; // 文本框 的 控制 final TextEditingController _textEditingController = TextEditingController(); @override void initState() { // TODO: implement initState super.initState(); // 初始化 _focusNode = FocusNode(); // 添加 点击 事件 _textEditingController.addListener(() { // 判断 是否 显示 var isVisible = _textEditingController.text.isNotEmpty; // 不是空的 false 不显示 // 更新 按钮 _updateDelIconVisible(isVisible); }); } //... _updateDelIconVisible(bool isVisible) { setState(() { // 点击 取反 _offstAge = !isVisible; }); } @override void dispose() { // TODO: implement dispose super.dispose(); _focusNode.unfocus(); } @override Widget build(BuildContext context) { return SizedBox( width: double.infinity, height: 30, child: Row( children: [ Expanded( flex: 1, child: Container( height: double.infinity, // 框框 总体 向左走 margin: EdgeInsets.only(left: 12), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(4)), child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ Padding(padding: EdgeInsets.only(left: 8)), // Icon(Icons.search_rounded,color: Colors.black,), // Padding(padding: EdgeInsets.only(left: 8)), Expanded( flex: 1, child: TextField( controller: _textEditingController, keyboardType: TextInputType.text, autofocus: false, focusNode: _focusNode, style: TextStyle( fontSize: 14, color: Colors.black, ), decoration: InputDecoration( // 输入框 文字的 位置 contentPadding: EdgeInsets.only(top: 8.0, left: 8.0), hintText: widget.hintLabel, // 前面 的 图标 prefixIcon: GestureDetector( child: Icon(Icons.search_rounded), ), // 后面的图标 suffixIcon: GestureDetector( onTap: () { _textEditingController.clear(); _focusNode.unfocus(); }, child: Icon(Icons.cancel_outlined), ), border: OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(5)), )), maxLines: 1, )), //配置 显示 与不显示 // Offstage( // offstage: _offstAge, // child: GestureDetector( // onTap: () => { // _textEditingController.clear() // }, // child: Icon(Icons.cancel_outlined,color: Colors.black,)), // ), Padding(padding: EdgeInsets.only(right: 8)), ], ), )), GestureDetector( onTap: () { _focusNode.unfocus(); }, child: Container( padding: const EdgeInsets.only(left: 16, right: 16), child: Text("取消", style: TextStyle(fontSize: 16, color: Colors.black)), ), ), ], ), ); } }
使用
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( // padding titleSpacing: 0, toolbarHeight: 44, backgroundColor: Colors.white, elevation: 0, title: SearchAppBar( hintLabel:"电影/电视剧/影视" ), ), body: ListView( children: [ ], ), ); }
底部选择
_modelBottomSheet() { showModalBottomSheet( context: this.context, builder: (BuildContext context) { return Container( height: 200, width: double.infinity, child: Column( children: [ Container( alignment: Alignment.center, height: 44, child: Text("this is title"), ), Expanded(child: Text("this is content")), Container( height: 1, color: Colors.grey[200], ), Container( height: 64, child: Row( children: [ Expanded( child: TextButton( onPressed: () {}, child: Text("cancel"), )), Container(width: 1, color: Colors.grey[200]), Expanded( child: TextButton( onPressed: () {}, child: Text("confirm"))), ], ), ), ], ), ); }); }
路由点击跳转
GestureDetector( onTap: (){ setState(() { Navigator.of(context).pushNamed("/personPage"); }); }, child: Column( children: [ Icon(Icons.home), Text("主页"), ], ), ),
得到输入框的值
TextEditingController _textEditingController = new TextEditingController();
children: [ TextField( controller: _textEditingController, ), ElevatedButton(onPressed: (){ print("${_textEditingController.text}"); }, child: Text("得到输入框的值")) ],
写一个widget的 步骤
先创建 数据
import 'package:flutter/material.dart'; class FunctionButtonItem { final String imgUri; final String title; final Function? onTapHandle; FunctionButtonItem(this.imgUri, this.title, this.onTapHandle); } void defaultTapHandler(context) {} final List<FunctionButtonItem> list = [ FunctionButtonItem("assets/images/start.jpg", "看星星", null), FunctionButtonItem("assets/images/eat.jpeg", "吃东西", null), FunctionButtonItem("assets/images/face.jpg", "去上学", null), FunctionButtonItem("assets/images/battle.jpg", "决斗", null), FunctionButtonItem("assets/images/noodle.jpg", "面条", null), FunctionButtonItem("assets/images/sleep.webp", "躺着", null), FunctionButtonItem("assets/images/houseManage.jpg", "管理", (context) { bool isLogin = true; if (isLogin) { Navigator.pushNamed(context, "/login"); return; } else {} Navigator.pushNamed(context, "/login"); }), FunctionButtonItem("assets/images/sleep2.jpg", "睡觉", null), ];
使用数据
import 'package:flutter/material.dart'; import 'ButtonItem.dart'; class FunctionButtonWidget extends StatelessWidget { final FunctionButtonItem data; const FunctionButtonWidget(this.data, {Key? key}) : super(key: key); @override Widget build(BuildContext context) { return GestureDetector( onTap: (){ if(null != data.onTapHandle){ data.onTapHandle!(context); } }, child: Container( decoration: BoxDecoration( // color: Colors.black12 ), margin: EdgeInsets.only(top: 20,left: 2.6), child: Column( children: [ Image.asset( data.imgUri, width: MediaQuery.of(context).size.width * 0.32, height: 40, ), Padding(padding: EdgeInsets.only(top: 3)), Text(data.title), ], ), ), ); } }
封装成 widget
import 'package:flutter/material.dart'; import 'package:flutter03/components/buttonItem/function_button_widget.dart'; import 'ButtonItem.dart'; class FunctionButton extends StatelessWidget { const FunctionButton({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Container( child: Wrap( spacing: 2.0, runSpacing: 1.0, children: list.map((item) => FunctionButtonWidget(item)).toList(), ), ); } }
FunctionButton(),
大demo
思路且代码
初始化 图片列表
@override void initState() { super.initState(); list = [ "imgs/1.png", "imgs/laying.jpg", "imgs/noodle.jpg", ]; }
banner 组件
轮播图的 长宽高 参数
final double width; final double height; final List<String> list; const BannerPage( {super.key, this.height = 400, this.width = 400, required this.list});
组件 里面 的参数
int _currentIndex = 0; List<Widget> pageList = []; late PageController _pageController; late Timer timer;
初始化
@override void initState() { super.initState(); // pagecontroller _pageController = PageController(initialPage: 0); // 设置 循环时间 的 api 定时器 timer = Timer.periodic(Duration(seconds: 2), (t) { _pageController.animateToPage( // 当前 页面 +1 余 数组的长度 _currentIndex+1 % pageList.length, // duration duration: Duration(milliseconds: 300), // 动画 显示的样子 curve: Curves.linear); }); //data for (var i = 0; i < widget.list.length; i++) { // 添加 图片 pageList.add(ImagePage( src: widget.list[i], width: widget.width, height: widget.height, )); } }
图片组件
class ImagePage extends StatelessWidget { final double width; final double height; final String src; const ImagePage( {super.key, this.width = double.infinity, this.height = 200, required this.src}); @override Widget build(BuildContext context) { // 定义 组件 设置 属性 在 组件 里面 return SizedBox( width: width, height: height, child: Image.asset(src, fit: BoxFit.cover), // child: Text("hello,world"), ); } }
主要 代码
SizedBox( height: 400, width: 400, // 页面 编辑 child: PageView.builder( // 需要加 控制器 controller: _pageController, // 图片 改变时 onPageChanged: (index) { setState(() { // index 为 参数 得到的值 为 当前 图片 的下标 // 0 , 1,2,3,4 _currentIndex = index % pageList.length; }); }, // 页面 大小 itemCount: 1000, itemBuilder: (context, index) { //0 % 3 = 0 1 %3 = 1 2 % 3 = 2 3 % 3 = 1 return pageList[index % pageList.length]; }, ), ),
配置园点
Positioned( // right 加 left 都 为 0 就会 占满 整行 // 之后 便可 居中 left: 180, bottom: 2, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: List.generate(3, (index) { return Container( margin: EdgeInsets.all(5), width: 10, height: 10, decoration: BoxDecoration( // 小点 的 判断 color: _currentIndex == index ? Colors.blue : Colors.grey, // borderRadius: BorderRadius.circular(5) shape: BoxShape.circle), ); }).toList(), ), )
全部代码
编写viewTest 页面
import 'package:flutter/material.dart'; import '../pages/widget/banner.dart'; class ViewTest extends StatefulWidget { const ViewTest({Key? key}) : super(key: key); @override State<ViewTest> createState() => _ViewTestState(); } class _ViewTestState extends State<ViewTest> { List<String> list = []; @override void initState() { super.initState(); list = [ "imgs/1.png", "imgs/laying.jpg", "imgs/noodle.jpg", ]; } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('hello,flutter'), ), body: ListView( children: [BannerPage(list: list)], ), ); } }
在 main 里面 配置
路 由
@override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, // home: Tabs(), initialRoute: '/', routes: { "/":(context)=>const Tabs(), "/news":(context)=>const NewsPage(), "/viewtest":(context)=> ViewTest(), "/form":(context)=>const DialogPage(), "/login":(context)=>const LoginPage(), "/register":(context)=>const RegisterPage() }, ); }
编写banner页面的代码
import 'dart:async'; import 'package:flutter/material.dart'; class BannerPage extends StatefulWidget { final double width; final double height; final List<String> list; const BannerPage( {super.key, this.height = 400, this.width = 400, required this.list}); @override State<BannerPage> createState() => _BannerPageState(); } class _BannerPageState extends State<BannerPage> { int _currentIndex = 0; List<Widget> pageList = []; late PageController _pageController; late Timer timer; @override void initState() { super.initState(); // pagecontroller _pageController = PageController(initialPage: 0); timer = Timer.periodic(Duration(seconds: 2), (t) { _pageController.animateToPage(_currentIndex+1 % pageList.length, duration: Duration(milliseconds: 300), curve: Curves.linear); }); //data for (var i = 0; i < widget.list.length; i++) { pageList.add(ImagePage( src: widget.list[i], width: widget.width, height: widget.height, )); } } // 销毁 @override void dispose(){ super.dispose(); // 取消 计时器 timer.cancel(); _pageController.dispose(); } @override Widget build(BuildContext context) { return Stack( children: [ SizedBox( height: 400, width: 400, child: PageView.builder( controller: _pageController, onPageChanged: (index) { setState(() { _currentIndex = index % pageList.length; }); }, itemCount: 1000, itemBuilder: (context, index) { //0 % 3 = 0 1 %3 = 1 2 % 3 = 2 3 % 3 = 1 return pageList[index % pageList.length]; }, ), ), Positioned( // right 加 left 都 为 0 就会 占满 整行 // 之后 便可 居中 left: 180, bottom: 2, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: List.generate(3, (index) { return Container( margin: EdgeInsets.all(5), width: 10, height: 10, decoration: BoxDecoration( // 小点 的 判断 color: _currentIndex == index ? Colors.blue : Colors.grey, // borderRadius: BorderRadius.circular(5) shape: BoxShape.circle), ); }).toList(), ), ) ], ); } } class ImagePage extends StatelessWidget { final double width; final double height; final String src; const ImagePage( {super.key, this.width = double.infinity, this.height = 200, required this.src}); @override Widget build(BuildContext context) { // 定义 组件 设置 属性 在 组件 里面 return SizedBox( width: width, height: height, child: Image.asset(src, fit: BoxFit.cover), // child: Text("hello,world"), ); } }