Bloc问题:使用Bloc的时候,有一个让我至今为止十分在意的问题,无法真正的跨页面交互!在反复的查阅官方文档后,使用一个全局Bloc的方式,实现了“伪”跨页面交互。fishRedux,在中小型项目中使用时,会降低一定开发效率,但是在大型项目中,是非常好的
Getx优势:
- 路由管理
- build刷新方式
- 跨页面交互,在StreamController篇介绍过,当时是通过它实现的
- 实现了全局BuildContext
- 国际化,主题实现
GetMaterialApp 对于路由、snackbar、国际化、bottomSheet、对话框以及与路由相关的高级apis和没有上下文(context)的情况下是起重要作用的,它只是一个Widget,它的子组件是默认的MaterialApp。
get: ^4.1.4
导包
import 'package:get/get.dart';
在MaterialApp之前添加“获取”,将其转换为GetMaterialApp
Get.to()
,Get.back()
依此类推)时,才需要执行此步骤。如果您不使用它,则无需执行步骤11.动态路由Get.to
Get.to(TestAPage());
2.替换路由replace
Get.off(NextScreen());
3.返回 Navigator.pop(context
Get.back();
4.返回根,取消前面所有路线
Get.offAll(NextScreen());
5.静态路由
Get.toNamed('/details');
6.发送前一个路由的数据
Get.back(result: 'success');
7.Get提供高级动态URL
Get.offAllNamed("/NextScreen?device=phone&id=354&name=Enzo");
print(Get.parameters['id']);
// out: 354
print(Get.parameters['name']);
// out: Enzo
8.在系统Dialog上跳转页面,可以这样写
Get.to(XxxxPage(), preventDuplicates: false);
// 或者
Get.toNamed('xxx', preventDuplicates: false);
9.传参
Get.toNamed("/home/list/detail", arguments: {"id": 999})');
A->B,B传参回A,A获取值,类似于Android中startActivityForResult
//动态 路由的方式
Navigator.of(context).push(
new MaterialPageRoute(
builder: (BuildContext context) {
return new TestA();
},
),
).then((value) {
//获取上一个页面的数据
});
//Gex 动态的方式 获取数据
void fun5() async {
//动态 态的方式 并获取A页面的返回值
var value = await Get.to(new TestA());
print("A页面的返回值 $value");
}
退出登录时,关闭所有的页面,然后打开登录页面
Navigator.of(context).pushAndRemoveUntil(
new MaterialPageRoute(
builder: (BuildContext context) {
return new TestAPage();
},
),
(Route route) => false,
);
Get.offAll(new TestAPage());
代码
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'MyHomePage.dart';
import 'TestAPage.dart';
//程序入口
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
//使用 GetX第一步
return GetMaterialApp(
theme: ThemeData(
primarySwatch: Colors.yellow,
highlightColor: Color.fromRGBO(255, 255, 255, 0.5),
splashColor: Colors.blue),
//静态路径
routes: {
"/testa": (context) => TestAPage(),
},
//默认显示的首页页面
home: MyHomePage(),
);
}
}
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class TestAPage extends StatefulWidget {
TestAPage({Key key}) : super(key: key);
@override
_TestAPageState createState() {
return _TestAPageState();
}
}
class _TestAPageState extends State {
@override
void initState() {
super.initState();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return Center(
child: Container(
color: Colors.red,
child: OutlinedButton(
onPressed: (){
Get.back();
},
),
),
);
}
}
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
@override
_MyHomePageState createState() {
return _MyHomePageState();
}
}
class _MyHomePageState extends State {
@override
void initState() {
super.initState();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("首页"),
),
body: Center(
child: Container(
width: 200,
height: 200,
color: Colors.red,
child: OutlinedButton(
onPressed: (){
Get.toNamed("/testa");
},
),
),
),
);
}
}
注意:使用Getx之后,基本不需要使用StatelessWidget,提出疑问?那是widget是怎么改变状态的了,怎么点击事情,onPress
答:StatelessWidget仅仅只是代表自身无状态,无setState,不能更改自身的widget,但是不代表子widget没有自身的状态,他们如果自身是statefulWidget,
则说明,子widget可以改变自身的状态,这也是一种实现局部刷新的方式
class StateTest extends StatelessWidget {
StateTest({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
// TODO: implement build
return Center(
child: Container(
width: 200,
height: 200,
color: Colors.red,
child: OutlinedButton(
onPressed: (){
print("StateTest");
},
),
),
);
}
}
点击时:onPressed会被执行,但是StateTest自身widget不能被改变,无法执行自身的setStatus
import 'package:flutter_first/day54Getx/day2/PersonMy.dart';
import 'package:get/get.dart';
class CountController extends GetxController{
//声明为被观察者
var _count = 0.obs;
var _myPerson = PersonMy('zhangsan', 20).obs;
RxInt get getCount => _count;
void addCount() {
_count++;
}
}
GetX(
//初始化控制器
init: CountController(),
//监听回调
builder: (CountController controller) {
return Text("当前 count 的值为 ${controller.getCount}");
},
),
Obx(() {
return Text(
"Obx 当前 count 的值为 ${Get.find().getCount}");
}),
——————这种更新都是局部更新,实现了Provider中Selector的功能
注意:使用依赖管理时,应该使用Get.put
Controller controller = Get.put(Controller()); // Rather Controller controller = Controller();
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'CountObsMainPage.dart';
//程序入口
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
MyApp({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
// TODO: implement build
return GetMaterialApp(
theme: ThemeData(
primarySwatch: Colors.blue,
),
//默认显示的首页页面
home: CountObsMainPage(),
);
}
}
import 'package:flutter_first/day54Getx/day2/PersonMy.dart';
import 'package:get/get.dart';
///第一步定义 Controller
class CountController extends GetxController {
//声明为被观察者
var _count = 0.obs;
var _age = 0.obs;
get age => _age;
var _myPerson = PersonMy('zhangsan', 20).obs;
RxInt get getCount => _count;
//操作方法
void addCount() {
_count++;
}
void addAge() {
_age++;
}
}
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'CountController.dart';
class CountObsMainPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Gex 响应编程"),
),
backgroundColor: Colors.white,
///填充布局
body: Container(
padding: EdgeInsets.all(30),
width: double.infinity,
height: double.infinity,
child: Column(
children: [
GetX(
//初始化控制器
init: Get.put(CountController()),
//监听回调
builder: (CountController controller) {
return Text("当前 count 的值为 ${controller.getCount}");
},
),
//观察者自动更新
Obx(() {
return Text(
"Obx 当前 count 的值为 ${Get.find().getCount}");
}),
Obx(() {
return Text("Obx2 ${Get.find().age}");
})
],
)),
//点击按钮修改值
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
Get.find().addCount();
},
),
);
}
}
当前页面
final Controller c = Get.put(Controller());
则,可以通过Get找到一个正在被其他页面使用的Controller,并将它返回给你。
final Controller c = Get.find();
GetxController 无需自行手动释放,原因 :
在Getx中,Getx继承StatefulWidget,执行了GetInstance().delete(tag: widget.tag);
@override
void dispose() {
if (widget.dispose != null) widget.dispose!(this);
if (_isCreator! || widget.assignId) {
if (widget.autoRemove && GetInstance().isRegistered(tag: widget.tag)) {
GetInstance().delete(tag: widget.tag);
}
}
_subs.cancel();
_observer!.close();
controller = null;
_isCreator = null;
super.dispose();
}
如果使用到Getx的obs,则全部整体需要使用GetX.to ,toNamed那一套,否则可能会出错,原因
通过上面会在GetPage注册可知,说明在我们跳转页面的时候,GetX会拿你到页面信息存储起来,加以管理,下面俩种场景会导致GetxController无法释放
A页面和B页面能找到相同的GetxController,然后B页面在GetxController更新数据,GetxController则通过发布订阅模式,将数据发送到A页面,起始本质与StreamController相同
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'CountObsMainPage01.dart';
//程序入口
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
MyApp({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
// TODO: implement build
return GetMaterialApp(
theme: ThemeData(
primarySwatch: Colors.blue,
),
//默认显示的首页页面
home: CountObsMainPage01(),
);
}
}
import 'package:flutter_first/day54Getx/day2/PersonMy.dart';
import 'package:get/get.dart';
///第一步定义 Controller
class CountController01 extends GetxController {
//声明为被观察者
var _count = 0.obs;
var _age = 0.obs;
get age => _age;
var _myPerson = PersonMy('zhangsan', 20).obs;
RxInt get getCount => _count;
//操作方法
void addCount() {
_count++;
}
void addAge() {
_age++;
}
}
import 'package:flutter/material.dart';
import 'package:flutter_first/day54Getx/day3/CountObsMainPage02.dart';
import 'package:get/get.dart';
import 'CountController01.dart';
class CountObsMainPage01 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Gex 响应编程"),
),
backgroundColor: Colors.white,
///填充布局
body: Container(
padding: EdgeInsets.all(30),
width: double.infinity,
height: double.infinity,
child: Column(
children: [
GetX(
//初始化控制器
init: Get.put(CountController01()),
//监听回调
builder: (CountController01 controller) {
return Text("当前 count 的值为 ${controller.getCount}");
},
),
//观察者自动更新
Obx(() {
return Text(
"Obx 当前 count 的值为 ${Get.find().getCount}");
}),
Obx(() {
return Text("Obx2 ${Get.find().age}");
}),
OutlinedButton(
onPressed: () {
Get.to(CountObsMainPage02());
},
child: Text('进入2页面'))
],
)),
//点击按钮修改值
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
Get.find().addCount();
},
),
);
}
}
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'CountController01.dart';
class CountObsMainPage02 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Gex 响应编程"),
),
backgroundColor: Colors.white,
///填充布局
body: Container(
padding: EdgeInsets.all(30),
width: double.infinity,
height: double.infinity,
child: Column(
children: [
//观察者自动更新
Obx(() {
return Text(
"Obx 当前 count 的值为 ${Get.find().getCount()}");
}),
OutlinedButton(
onPressed: () {
Get.find().addCount();
},
child: Text("点击"))
],
)),
//点击按钮修改值
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
Get.find().addCount();
},
),
);
}
}
——————B页面点击时,A页面也会发生改变
可以提升性能,但不太常用,主动观察,类似于Provider中notifyListeners,主动通知
通过GetBuilder实现响应式控件,控制器必须继承自GetxController,所定义的目标状态变量之后无需后缀".obs ",但需要定义方法update;并且加载指定控制器,不仅需要使用Get.put进行注入,而且GetBuilder还需要通过指定泛型绑定目标注入的控制器。
GetBuilder(
builder: (CountController controller) {
return Text("当前Height值${controller.height}");
},
),
import 'package:flutter_first/day54Getx/day2/PersonMy.dart';
import 'package:get/get.dart';
///第一步定义 Controller
class CountController extends GetxController {
//声明为被观察者
var _count = 0.obs;
var _age = 0.obs;
var _height = 0;
get height => _height;
get age => _age;
var _myPerson = PersonMy('zhangsan', 20).obs;
RxInt get getCount => _count;
get controller => Get.find();
//操作方法
void addCount() {
_count++;
}
void addAge() {
_age++;
}
void addHeight() {
_height++;
update();
}
}
唯一的ID
如果你想用GetBuilder完善一个widget的更新控件,你可以给它们分配唯一的ID。不太常用
GetBuilder(
id: 'text' 、、这里
init: Controller(), // 每个控制器只用一次
builder: (_) => Text(
'${Get.find().counter}', //here
),
),
对比Getx vs obs vs GetBuilder
GetX比其他响应式状态管理器还是比较高效的,但它比GetBuilder多消耗一点内存。思前想后,以最大限度地消耗资源为目标,Obx应运而生。与GetX和GetBuilder不同的是,你将无法在Obx内部初始化一个控制器,它只是一个带有StreamSubscription的Widget,接收来自你的子代的变化事件,仅此而已。它比GetX更高效,但输给了GetBuilder,这是意料之中的,因为它是响应式的,而且GetBuilder有最简单的方法,即存储widget的hashcode和它的StateSetter。使用Obx,你不需要写你的控制器类型,你可以从多个不同的控制器中监听到变化,但它需要在之前进行初始化,或者使用本readme开头的示例方法,或者使用Bindings类。
///每次`count1`变化时调用。
ever(_count, (callback) {
print("$callback has been changed");
});
///只有在变量$_第一次被改变时才会被调用。
once(_age, (callback) => print("$callback was changed once"));
///防DDos - 每当用户停止输入1秒时调用,debounce将只发送最后一个事件。
debounce(_count, (callback) => print("debouce$callback"), time: Duration(seconds: 3));
///忽略1秒内的所有变化。
interval(_age, (callback) => print("interval $callback"), time: Duration(seconds: 1));
——————个人觉得debounce最有用,用于搜索时,当连续输入时,只用最后一次的进行网络请求搜索
这样,不影响其他的状态管理器
class Controller extends GetxController {
StreamController user = StreamController();
StreamController name = StreamController();
@override
void onInit() {
super.onInit();
}
///关闭流用onClose方法,而不是dispose
@override
void onClose() {
user.close();
name.close();
super.onClose();
}
}
这个挺有用的,封装了StatelessWidget,方便简洁使用,获取controller
abstract class GetView extends StatelessWidget {
const GetView({Key? key}) : super(key: key);
final String? tag = null;
T get controller => GetInstance().find(tag: tag)!;
@override
Widget build(BuildContext context);
}
Get.put(SomeClass());
Get.put(LoginController(), permanent: true);
Get.put(ListItemController, tag: "some unique string");
Get.put(
// 必备:你想得到保存的类,比如控制器或其他东西。
// 注:"S "意味着它可以是任何类型的类。
S dependency
// 可选:当你想要多个相同类型的类时,可以用这个方法。
// 因为你通常使用Get.find()来获取一个类。
// 你需要使用标签来告诉你需要哪个实例。
// 必须是唯一的字符串
String tag,
// 可选:默认情况下,get会在实例不再使用后进行销毁
// (例如:一个已经销毁的视图的Controller)
// 但你可能需要这个实例在整个应用生命周期中保留在那里,就像一个sharedPreferences的实例或其他东西。
//所以你设置这个选项
// 默认值为false
bool permanent = false,
// 可选:允许你在测试中使用一个抽象类后,用另一个抽象类代替它,然后再进行测试。
// 默认为false
bool overrideAbstract = false,
// 可选:允许你使用函数而不是依赖(dependency)本身来创建依赖。
// 这个不常用
InstanceBuilderCallback builder,
)
——————————tag的使用场景,同一个Controller时,可以使用,区别开来
——————————permanent : 生命周期,在整个应用中
可以懒加载一个依赖,这样它只有在使用时才会被实例化。 在Get.find时才会被调用
————————创建新的Controller, 使用不多
但里面没有 “逻辑”。它只是通知GetX的依赖注入系统,这个子类不能从内存中删除。
所以这对保持你的 "服务 "总是可以被Get.find()
获取到并保持运行是超级有用的。比如
ApiService
,StorageService
,CacheService
。
Bindings,常与Get.lazyPut/GetView一起使用,达到简洁的目的,这样可以不必要在Widget中写入Controller
1.定义Controller
import 'package:get/get.dart';
class HomeController extends GetxController {
var _count = 0.obs;
var _age = 0.obs;
get count => _count;
set count(value) {
_count = value;
} //操作方法
void addCount() {
_count++;
}
void addAge() {
_age++;
}
get age => _age;
set age(value) {
_age = value;
}
}
2.定义HomeBinding
import 'package:get/get.dart';
import 'HomeController.dart';
class HomeBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut(() => HomeController());
}
}
3.定义GetView
import 'package:get/get.dart';
import 'package:flutter/material.dart';
import 'HomeController.dart';
class HomeView extends GetView {
@override
Widget build(BuildContext context) {
// TODO: implement build
return Container(
decoration: BoxDecoration(
color: Colors.white,
image: DecorationImage(
fit: BoxFit.cover,
colorFilter: ColorFilter.linearToSrgbGamma(),
image: NetworkImage(
"https://images.pexels.com/photos/3902882/pexels-photo-3902882.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940"),
),
),
child: Scaffold(
appBar: AppBar(
title: Text("Gex 响应编程"),
),
backgroundColor: Colors.white,
body: buildWidget(),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
controller.addCount();
controller.addAge();
},
),
),
);
}
Widget buildWidget() {
return Container(
padding: EdgeInsets.all(30),
width: double.infinity,
height: double.infinity,
child: Column(
children: [
GetBuilder(
builder: (HomeController controller) {
return Text("当前Height值${controller.count}");
},
),
GetX(
//监听回调
builder: (HomeController controller) {
return Text("当前 count 的值为 ${controller.count}");
},
),
//观察者自动更新
Obx(() {
return Text("Obx 当前 count 的值为 ${controller.age}");
}),
Obx(() {
return Text("Obx2 ${controller.age}");
})
],
));
}
}
4.将GetMaterialApp设置为widget视图顶端
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:flutter/cupertino.dart';
import 'AppPages.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
MyApp({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
// TODO: implement build
return GetMaterialApp(
initialRoute: AppPages.INITIAL,
getPages: AppPages.routes,
);
}
}
5.路由管理
import 'package:flutter_first/day54Getx/day4/routes/app_pages.dart';
import 'package:get/get.dart';
import 'HomeBinding.dart';
import 'HomeView.dart';
class AppPages {
static const INITIAL = Routes.HOME;
static final routes = [
GetPage(
name: Routes.HOME,
page: () => HomeView(),
binding: HomeBinding(),
// children: [
// GetPage(
// name: Routes.COUNTRY,
// page: () => CountryView(),
// children: [
// GetPage(
// name: Routes.DETAILS,
// page: () => DetailsView(),
// ),
// ],
// ),
// ]
),
];
}
6.route
part of 'app_pages.dart';
abstract class Routes {
static const HOME = '/home';
static const COUNTRY = '/country';
static const DETAILS = '/details';
}
6.argument传值
import 'package:flutter_first/day54Getx/day4/routes/app_pages.dart';
import 'package:get/get.dart';
import 'CountryView.dart';
import 'HomeBinding.dart';
import 'HomeView.dart';
class AppPages {
static const INITIAL = Routes.HOME;
static final routes = [
GetPage(
name: Routes.HOME,
page: () => HomeView(),
binding: HomeBinding(),
children: [
GetPage(
name: Routes.COUNTRY,
page: () => CountryView(),
),
]
),
];
}
HomeView:
OutlinedButton(
onPressed: () {
Get.toNamed('/home/country',
arguments: {'name': 'zhangsan', 'age': 40});
},
child: Text('点击'))
countryView
final country = Get.arguments as Map; //获取传输值