Skip to content

Instantly share code, notes, and snippets.

@roipeker
Created September 22, 2020 00:29
Show Gist options
  • Save roipeker/fc6ba426a72c1aa59406ec4773b05cd0 to your computer and use it in GitHub Desktop.
Save roipeker/fc6ba426a72c1aa59406ec4773b05cd0 to your computer and use it in GitHub Desktop.
UIController, local state concept to replace stateful (not working properly)
import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:roi_getx_samples/custom_widget/ui_builder.dart';
extension MyVisible on Widget {
visible(bool flag, {bool maintainSize = false}) {
return Visibility(
child: this,
maintainState: true,
maintainAnimation: true,
maintainInteractivity: false,
maintainSize: maintainSize,
visible: flag,
);
}
}
main() {
runApp(GetMaterialApp(
initialBinding: BindingsBuilder(() {
Get.put(_MyHomeController());
}),
home: _MyHome(),
));
}
class MyModel {
final active = false.obs;
final int index;
var key = UniqueKey();
MyModel(this.index);
}
class _MyHomeController extends GetxController {
final showing = false.obs;
final myToggles = List.generate(3, (index) => MyModel(index)).obs;
removeIndex(int index) {
myToggles.removeWhere((m) => m.index == index);
}
removeModel(MyModel model) {
myToggles.remove(model);
}
}
class _MyHome extends GetView<_MyHomeController> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Expanded(
child: Obx(
() =>
///OPTION 1
ListView.separated(
itemBuilder: (ctx, idx) {
final model = controller.myToggles[idx];
return Obx(
() => MySwitch(
key: model.key,
index: model.index,
onChange: model.active,
onRemove: () => controller.removeModel(model),
// onRemove: controller.removeIndex,
isActive: model.active(),
),
);
},
separatorBuilder: (ctx, idx) => Divider(height: 1),
itemCount: controller.myToggles.length,
),
/// OPTION 2
// ListView(
// children: controller.myToggles
// .map(
// (model) => MySwitch(
// key: model.key,
// onChange: model.active,
// index: model.index,
// onRemove: () => controller.removeModel(model),
// isActive: model.active(),
// ),
// )
// .toList(),
// ),
),
),
],
),
);
}
}
class MySwitchController extends UIController<MySwitch> {
final rebuilds = 0.obs;
MySwitchController() {
print("new MySwitchController()");
}
@override
void didUpdateWidget(MySwitch oldie, MySwitch newbie) {
if (oldie != newbie) {
rebuilds.value++;
}
}
@override
didChangeDependencies(s) {
print("CHANGE DEP");
}
bool isActive = false;
void toggleActive() {
isActive = !isActive;
update();
}
@override
onInit() {
print("Initializing $hashCode");
}
@override
FutureOr onClose() {
print('on closed');
rebuilds.close();
// Get.snackbar('Item borrado', 'Item ${widget.index} removed');
// print('Item borrado - Item ${widget.index} removed');
}
}
class MySwitch extends UIView<MySwitchController> {
final bool isActive;
final Function(bool) onChange;
final Function() onRemove;
// final Function(int) onRemove;
final int index;
const MySwitch({
Key key,
this.index,
this.isActive = false,
this.onChange,
this.onRemove,
}) : super(key: key);
@override
get params => [isActive, index]; //onChange,
@override
get initController => MySwitchController();
@override
Widget render(controller) {
return Container(
height: 30,
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Container(
color: Colors.black,
width: 90,
alignment: Alignment.center,
child: Obx(() {
final rebuilds = controller.rebuilds.toString().padLeft(2, '0');
return Text(
'$index - rebuilds: $rebuilds ',
style: const TextStyle(
fontSize: 10,
fontWeight: FontWeight.bold,
color: Colors.white,
),
);
}),
),
const VerticalDivider(width: 1),
GestureDetector(
onTap: () => onChange.call(!isActive),
child: _buildBox('WIDGET', isActive),
),
const VerticalDivider(width: 1),
GestureDetector(
onTap: controller.toggleActive,
child: _buildBox('CONTROLLER', controller.isActive),
),
FlatButton(
// onPressed: () => onRemove(index),
onPressed: onRemove,
minWidth: 48,
child: Icon(Icons.delete),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
padding: EdgeInsets.zero,
),
],
),
);
}
Widget _buildBox(String label, bool flag) {
return Container(
width: 90,
color: flag ? Colors.green : Colors.red,
alignment: Alignment.center,
child: Text(
label,
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
);
}
}
import 'package:flutter/material.dart';
import 'package:get/get.dart';
abstract class EqualView {
List<Object> get params => null;
bool equals(EqualView o) {
if (params == null || o.params == null) return false;
for (var i = 0; i < params.length; ++i) {
if (params[i] != o.params[i]) return false;
}
return true;
}
}
abstract class UIView<T extends UIController> extends StatelessWidget {
// with EqualView {
const UIView({Key key}) : super(key: key);
List<Object> get params => null;
bool equals(UIView o) {
if (params == null || o.params == null) return false;
for (var i = 0; i < params.length; ++i) {
if (params[i] != o.params[i]) return false;
}
return true;
}
@override
// ignore: invalid_override_of_non_virtual_member
bool operator ==(Object other) {
if (params != null && other is UIView) {
return equals(other);
}
return super == other;
}
@override
// ignore: invalid_override_of_non_virtual_member
int get hashCode {
if (params != null) {
return params.fold(
0, (previousValue, element) => previousValue ^ element.hashCode);
}
return super.hashCode;
}
T get initController;
@override
Widget build(BuildContext context) {
return UIBuilder<T, UIView>(
owner: this,
init: () => initController,
builder: (controller) => render(controller),
);
}
Widget render(T controller) {
return Container();
}
}
class UIController<T> extends GetxController {
void didChangeDependencies(State state) {}
void didUpdateWidget(T oldWidget, T newWidget) {}
void _didUpdateWidget(T oldWidget, T newWidget) {
_widget = newWidget;
didUpdateWidget(oldWidget, newWidget);
}
T _widget;
T get widget => _widget;
}
class UIBuilder<T extends UIController, S extends UIView>
extends StatefulWidget {
final Widget Function(T) builder;
final String id;
final bool autoRemove;
final void Function(State state) initState, dispose, didChangeDependencies;
final void Function(UIBuilder oldWidget, State state) didUpdateWidget;
final InstanceBuilderCallback<T> init;
final S owner;
const UIBuilder({
Key key,
this.owner,
this.init,
@required this.builder,
this.autoRemove = true,
this.initState,
this.dispose,
this.id,
this.didChangeDependencies,
this.didUpdateWidget,
}) : assert(builder != null),
super(key: key);
@override
_GetBuilderState<T, S> createState() => _GetBuilderState<T, S>();
}
class _GetBuilderState<T extends UIController, S extends UIView>
extends State<UIBuilder<T, S>> with GetStateUpdaterMixin {
VoidCallback remove;
// T get controller => widget.init;
UIController _controller;
@override
void initState() {
super.initState();
if (widget.initState != null) widget.initState(this);
_controller = widget.init.call();
_controller?.onStart();
_subscribeToController();
}
/// Register to listen Controller's events.
/// It gets a reference to the remove() callback, to delete the
/// setState "link" from the Controller.
void _subscribeToController() {
remove?.call();
remove = (widget.id == null)
? _controller?.addListener(getUpdate)
: _controller?.addListenerId(widget.id, getUpdate);
}
/// Sample for [GetStateUpdate] when you don't wanna
/// use [GetStateHelper mixin].
/// bool _getUpdater() {
/// final _mounted = mounted;
/// if (_mounted) setState(() {});
/// return _mounted;
/// }
@override
void dispose() {
super.dispose();
if (widget.dispose != null) widget.dispose(this);
if (widget.autoRemove) {
_controller?.onClose();
}
remove?.call();
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
_controller.didChangeDependencies(this);
if (widget.didChangeDependencies != null) {
widget.didChangeDependencies(this);
}
}
@override
void didUpdateWidget(oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.id != widget.id) {
_subscribeToController();
}
_controller._didUpdateWidget(oldWidget.owner, widget.owner);
if (widget.didUpdateWidget != null) widget.didUpdateWidget(oldWidget, this);
}
@override
Widget build(BuildContext context) => widget.builder(_controller);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment