flutter学习之基础组件(四)
本节主要记录StatefulWidget有状态组件和bottomNavigationBar组件实现主页面tab切换以及路由跳转,命名路由跳转。
文章目录
一、StatefulWidget 有状态组件
在 Flutter中自定义组件其实就是一个类,这个类需要继承StatelessWidget/StatefulWidget。
StatelessWidget是无状态组件,状态不可变的widget
StatefulWidget是有状态组件,持有的状态可能在widget生命周期改变。
通俗的讲:如果我 们想改变页面中的数据的话这个时候就需要用到StatefulWidget
1.使用StatefulWidget 实现一个简单的计数器
在StatefulWidget中想更新页面状态,就必须调用setState方法去刷新页面。
/**
* 计数器
*/
class HomeContent extends StatefulWidget {
const HomeContent({Key? key}) : super(key: key);
@override
_HomeContentState createState() => _HomeContentState();
}
class _HomeContentState extends State<HomeContent> {
int count = 0;
@override
Widget build(BuildContext context) {
return Column(
children: [
SizedBox(height: 200,),
Chip(label: Text("你好 $count")),
ElevatedButton(
onPressed: () {
setState(() {
count++;
});
},
child: Text("+1"),
),
],
);
}
}
2.使用StatefulWidget 动态添加列表
/**
* list 动态添加数据
*/
class ListStateContent extends StatefulWidget {
const ListStateContent({Key? key}) : super(key: key);
@override
_ListStateContentState createState() => _ListStateContentState();
}
class _ListStateContentState extends State<ListStateContent> {
List list = [];
int count = 0;
@override
Widget build(BuildContext context) {
return ListView(
children: [
Column(
children: this.list.map((value){
return ListTile(
title: Text(value),
subtitle: Text("这是内容"),
leading: Icon(Icons.ac_unit_sharp),
);
}).toList(),
),
SizedBox(height: 20,),
ElevatedButton(
onPressed: () {
setState(() {
list.add("data $count");
count++;
});
},
child: Text("添加数据"),
)
],
);
}
}
二、bottomNavigationBar组件
BottomNavigationBar是底部导航条,可以让我们定义底部Tab切换,bottomNavigationBar是Scaffold组件的参数。
效果:
1.BottomNavigationBar常见的属性
注:如果底部tab超过三个,必须设置type,否则会出现bug,显示问题
2.BottomNavigationBar实现可以左右切换页面的主页
先创建四个主页相关的页面
import 'package:flutter/cupertino.dart';
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return Center(
child: Text("首页"),
);
}
}
class MessagePage extends StatefulWidget {
const MessagePage({Key? key}) : super(key: key);
@override
_MessagePageState createState() => _MessagePageState();
}
class _MessagePageState extends State<MessagePage> {
@override
Widget build(BuildContext context) {
return Center(
child: Text("消息页面"),
);
}
}
class MinePage extends StatefulWidget {
const MinePage({Key? key}) : super(key: key);
@override
_MinePageState createState() => _MinePageState();
}
class _MinePageState extends State<MinePage> {
@override
Widget build(BuildContext context) {
return Center(
child: Text("我的页面"),
);
}
}
class SettingPage extends StatefulWidget {
const SettingPage({Key? key}) : super(key: key);
@override
_SettingPageState createState() => _SettingPageState();
}
class _SettingPageState extends State<SettingPage> {
@override
Widget build(BuildContext context) {
return Center(
child: Text("设置"),
);
}
}
在创建首页页面
class TestTen extends StatelessWidget{
@override
Widget build(BuildContext context) {
return MaterialApp(
home: TabState(index: 0,)
);
}
}
编写首页bottomNavigationBar相关逻辑
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_view_demo/demoFour/pages/page/HomePage.dart';
import 'package:flutter_view_demo/demoFour/pages/page/MessagePage.dart';
import 'package:flutter_view_demo/demoFour/pages/page/MinePage.dart';
import 'package:flutter_view_demo/demoFour/pages/page/SettingPage.dart';
class TabState extends StatefulWidget {
final index ;
TabState({Key? key, this.index = 0}) : super(key: key);
@override
_TabStateState createState() => _TabStateState(index);
}
class _TabStateState extends State<TabState> {
int _currentIndex = 0;
//tab对于页面集合
List<Widget> _pageList = [
HomePage(),
MessagePage(),
SettingPage(),
MinePage()
];
_TabStateState(int currentIndex){
this._currentIndex = currentIndex;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("BottemNativigationBar组件"),),
body: _pageList[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
// backgroundColor: Colors.red[600],
currentIndex: _currentIndex,
onTap: (int index){
setState(() {
_currentIndex = index; //更新tab切换状态
});
},
fixedColor: Colors.red, //选中的颜色
type: BottomNavigationBarType.fixed, //配置底部可以有多个按钮,否则超出3个会有问题
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text("首页")
),
BottomNavigationBarItem(
icon: Icon(Icons.message),
title: Text("消息")
),
BottomNavigationBarItem(
icon: Icon(Icons.settings),
title: Text("设置")
),
BottomNavigationBarItem(
icon: Icon(Icons.info_outline_sharp),
title: Text("我的")
)
],
),
);
}
}
三、Flutter中的路由
Flutter中的路由通俗的讲就是页面跳转。在Flutter中通过Navigator组件管理路由导航。 并提供了管理堆栈的方法。如:Navigator.push 和 Navigator.pop
Flutter 中给我们提供了两种配置路由跳转的方式:1、基本路由 2、命名路由
1.基本路由使用
基本路由使用有两种,一种是直接跳转,一种是传值跳转,参考代码
class TestEleven extends StatelessWidget{
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text("路由,跳转"),),
body: NavigatorState()
),
);
}
}
class NavigatorState extends StatefulWidget {
const NavigatorState({Key? key}) : super(key: key);
@override
_NavigatorStateState createState() => _NavigatorStateState();
}
class _NavigatorStateState extends State<NavigatorState> {
@override
Widget build(BuildContext context) {
return Center(
child: Column(
children: [
ElevatedButton(onPressed: (){
Navigator.of(context).push(
MaterialPageRoute(builder: (context)=>TestTen())
);
},
child: Text("普通路由跳转到主页")),
ElevatedButton(onPressed: (){
Navigator.of(context).push(
MaterialPageRoute(builder: (context)=>ListPage("跳转到列表页面,并且传值"))
);
},
child: Text("普通路由传值")),
],
),
);
}
}
class ListPage extends StatelessWidget{
late String _title;
//获取传过来的值
ListPage(this._title,{Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("搜索页面"),),
body: ListView(
children: [
ListTile(
//赋值
title: Text(_title),
),
ListTile(
title: Text(_title),
),
ListTile(
title: Text(_title),
),
ListTile(
title: Text(_title),
),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.of(context).pop(); //自定义返回
},
child: Text("返回"),
),
);
}
}
2.命名路由简单使用
命名路由跳转需要在MaterialApp下配置routes属性,routes里面配置跳转别名和跳转页面
class TestEleven extends StatelessWidget{
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text("路由,跳转"),),
body: NavigatorState()
),
routes: { //命名路由
"/TestTen" : (context) =>TestTen(),
"/ListPage" : (context) =>ListPage("跳转到列表页面,并且传值"),
},
);
}
}
class NavigatorState extends StatefulWidget {
const NavigatorState({Key? key}) : super(key: key);
@override
_NavigatorStateState createState() => _NavigatorStateState();
}
class _NavigatorStateState extends State<NavigatorState> {
@override
Widget build(BuildContext context) {
return Center(
child: Column(
children: [
ElevatedButton(onPressed: (){
Navigator.pushNamed(context, "/ListPage");
},
child: Text("命名路由传值")),
],
),
);
}
}
3…命名路由跳转传值
参考官网文档https://flutter.dev/docs/cookbook/navigation/navigate-with-arguments
官方介绍:
在Navigator提供了从使用公共标识符的应用程序的任何部分定位到一个命名的路由的能力。在某些情况下,您可能还需要将参数传递给命名路由。例如,您可能希望导航到该/user路线并将有关用户的信息传递到该路线。
您可以使用方法的arguments参数 完成此任务Navigator.pushNamed()。使用ModalRoute.of()方法或在onGenerateRoute() 提供给MaterialApp或CupertinoApp 构造函数的函数中提取参数 。
这个秘籍演示了如何将参数传递给命名路由ModalRoute.of() 并onGenerateRoute()使用以下步骤读取参数:
定义需要传递的参数。
创建一个提取参数的小部件。
在routes表中注册小部件。
导航到小部件。
这块功能具体可以抽取出来,统一管理一个跳转类:
定义一个跳转类Routes
import 'package:flutter/material.dart';
import 'package:flutter_view_demo/demoFour/TestTen.dart';
import 'package:flutter_view_demo/demoFour/pages/MingMinPage.dart';
import 'package:flutter_view_demo/demoFour/pages/user/Login.dart';
import 'package:flutter_view_demo/demoFour/pages/user/RegisterFirst.dart';
import 'package:flutter_view_demo/demoFour/pages/user/RegisterSecond.dart';
import 'package:flutter_view_demo/demoFour/pages/user/RegisterThird.dart';
import 'package:flutter_view_demo/demoThree/TestSeven.dart';
//配置路由,定义Map类型的routes,Key为String类型,Value为Function类型
final Map<String,Function> routes={
// '/':(context)=>Tabs(),
'/TestTen':(context)=>TestTen(),
'/TestSeven':(context)=>TestSeven(),
'/MingMinState':(context,{arguments})=>MingMinState(arguments:arguments),
'/LoginPage':(context,{arguments})=>LoginPage(),
'/RegisterFirstPage':(context,{arguments})=>RegisterFirstPage(),
'/RegisterSecondPage':(context,{arguments})=>RegisterSecondPage(),
'/RegisterThirdPage':(context,{arguments})=>RegisterThirdPage(),
};
//固定写法
var onGenerateRoute=(RouteSettings settings) {
//String? 表示name为可空类型
final String? name = settings.name;
//Function? 表示pageContentBuilder为可空类型
final Function? pageContentBuilder = routes[name];
if (pageContentBuilder != null) {
if (settings.arguments != null) {
final Route route = MaterialPageRoute(
builder: (context) =>
pageContentBuilder(context, arguments: settings.arguments));
return route;
}else{
final Route route = MaterialPageRoute(
builder: (context) =>
pageContentBuilder(context));
return route;
}
}
};
在MaterialApp中配置:
onGenerateRoute: onGenerateRoute, //配置命名路由
就实现了命名路由功能
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_view_demo/routes/Routes.dart';
class TestTwelve extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text("命名路由传值"),
),
body: NavigatorState()),
onGenerateRoute: onGenerateRoute, //配置命名路由
);
}
}
class NavigatorState extends StatefulWidget {
const NavigatorState({Key? key}) : super(key: key);
@override
_NavigatorStateState createState() => _NavigatorStateState();
}
class _NavigatorStateState extends State<NavigatorState> {
@override
Widget build(BuildContext context) {
return Center(
child: Column(
children: [
ElevatedButton(
onPressed: () {
Navigator.pushNamed(context, "/TestSeven");
},
child: Text("命名正常跳转")),
ElevatedButton(
onPressed: () {
Navigator.pushNamed(context, "/MingMinState",
arguments: {"id": 123, "name": "王"});
},
child: Text("命名路由传值")),
ElevatedButton(
onPressed: () {
Navigator.pushNamed(context, "/LoginPage");
},
child: Text("跳转到登录")),
ElevatedButton(
onPressed: () {
Navigator.pushNamed(context, "/RegisterFirstPage");
},
child: Text("跳转到注册")),
],
),
);
}
}
接收到传值,并处理
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class MingMinState extends StatefulWidget {
var arguments;
MingMinState({Key? key, this.arguments}) : super(key: key);
@override
_MingMinStateState createState() => _MingMinStateState(this.arguments);
}
class _MingMinStateState extends State<MingMinState> {
Map arguments;
_MingMinStateState(this.arguments);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("命名路由接受值"),),
body: Center(
child: Column(
children: [
Text(arguments["id"].toString()),
Text(arguments["name"]),
],
),
),
);
}
}
4.Flutter中返回到上一级页面
Navigator.of(context).pop(); //返回到上一页
5.替换路由
通过pushReplacementNamed跳转的页面,相当于下一个页面复用的上一页页面,点击返回,直接就返回到了根页面
Navigator.of(context).pushReplacementNamed('/RegisterSecondPage');
6.返回到根路由
下面代码如果执行了,先跳转到指定页面,然后会把这个页面之前的页面都置为空,就剩下一个页面,点击返回,直接就退出APP
Navigator.of(context).pushAndRemoveUntil(
new MaterialPageRoute(builder: (context) => new TabState(index:1)), (route) => route == null
);