我有一个包含listView
和floatingActionButton
的父级,我想在用户开始滚动时隐藏floatingActionButton
,但我设法在父级小部件中做到了这一点,但这需要每次重新构建列表。
我已将floatingActionButton
移到一个单独的类,因此我可以更新状态并仅重建该小部件,因为我遇到的问题是将数据从父类中的ScrollController
传递给孩子通过导航执行此操作很简单,但在不重建父级的情况下缝了一个但更尴尬的东西!
答案 0 :(得分:4)
您可以使用 StatefulBuilder 并使用其 setState 函数在其下构建小部件。
示例:
import 'package:flutter/material.dart';
class MyWidget extends StatefulWidget {
@override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
int count = 0;
@override
Widget build(BuildContext context) {
return Column(
children: [
// put widget here that you do not want to update using _setState of StatefulBuilder
Container(
child: Text("I am static"),
),
StatefulBuilder(builder: (_context, _setState) {
// put widges here that you want to update using _setState
return Column(
children: [
Container(
child: Text("I am updated for $count times"),
),
RaisedButton(
child: Text('Update'),
onPressed: () {
// Following only updates widgets under StatefulBuilder as we are using _setState
// that belong to StatefulBuilder
_setState(() {
count++;
});
})
],
);
}),
],
);
}
}
答案 1 :(得分:2)
当父级中的值发生变化时重建仅子级小部件的一种好方法是使用 ValueNotifier 和 ValueListenableBuilder。将 ValueNotifier
的实例添加到父级的 state 类中,并将要重建的小部件包装在 ValueListenableBuilder
中。
当您想要更改值时,请使用通知程序而不调用 setState
,然后子小部件将使用新值重建。
import 'package:flutter/material.dart';
class Parent extends StatefulWidget {
@override
_ParentState createState() => _ParentState();
}
class _ParentState extends State<Parent> {
ValueNotifier<bool> _notifier = ValueNotifier(false);
@override
Widget build(BuildContext context) {
return Column(
children: [
ElevatedButton(onPressed: () => _notifier.value = !_notifier.value, child: Text('toggle')),
ValueListenableBuilder(
valueListenable: _notifier,
builder: (BuildContext context, bool val, Widget child) {
return Text(val.toString());
}),
],
);
}
}
答案 2 :(得分:0)
为了获得最佳性能,您可以围绕Scaffold
创建自己的包装,该包装将body
作为参数。在body
中调用setState
时,不会重建HideFabOnScrollScaffoldState
小部件。
这是一种常见模式,也可以在诸如AnimationBuilder
之类的核心小部件中找到。
import 'package:flutter/material.dart';
main() => runApp(MaterialApp(home: MyHomePage()));
class MyHomePage extends StatefulWidget {
@override
State<StatefulWidget> createState() => MyHomePageState();
}
class MyHomePageState extends State<MyHomePage> {
ScrollController controller = ScrollController();
@override
Widget build(BuildContext context) {
return HideFabOnScrollScaffold(
body: ListView.builder(
controller: controller,
itemBuilder: (context, i) => ListTile(title: Text('item $i')),
),
floatingActionButton: FloatingActionButton(
onPressed: () {},
child: Icon(Icons.add),
),
controller: controller,
);
}
}
class HideFabOnScrollScaffold extends StatefulWidget {
const HideFabOnScrollScaffold({
Key key,
this.body,
this.floatingActionButton,
this.controller,
}) : super(key: key);
final Widget body;
final Widget floatingActionButton;
final ScrollController controller;
@override
State<StatefulWidget> createState() => HideFabOnScrollScaffoldState();
}
class HideFabOnScrollScaffoldState extends State<HideFabOnScrollScaffold> {
bool _fabVisible = true;
@override
void initState() {
super.initState();
widget.controller.addListener(_updateFabVisible);
}
@override
void dispose() {
widget.controller.removeListener(_updateFabVisible);
super.dispose();
}
void _updateFabVisible() {
final newFabVisible = (widget.controller.offset == 0.0);
if (_fabVisible != newFabVisible) {
setState(() {
_fabVisible = newFabVisible;
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: widget.body,
floatingActionButton: _fabVisible ? widget.floatingActionButton : null,
);
}
}
或者,您也可以为FloatingActionButton
创建一个包装器,但这可能会破坏过渡。
答案 3 :(得分:0)
我认为使用流更简单,也很容易。
您只需要在事件到达时将其发布到流中,然后使用流构建器来响应这些更改。
在这里,我基于小部件层次结构中的小部件焦点显示/隐藏组件。
我在这里使用了rxdart包,但我认为您不需要。还是您可能还是要这么做,因为大多数人还是会使用BloC模式。
import 'dart:async';
import 'package:rxdart/rxdart.dart';
class _PageState extends State<Page> {
final _focusNode = FocusNode();
final _focusStreamSubject = PublishSubject<bool>();
Stream<bool> get _focusStream => _focusStreamSubject.stream;
@override
void initState() {
super.initState();
_focusNode.addListener(() {
_focusStreamSubject.add(_focusNode.hasFocus);
});
}
@override
void dispose() {
_focusNode.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
_buildVeryLargeComponent(),
StreamBuilder(
stream: _focusStream,
builder: ((context, AsyncSnapshot<bool> snapshot) {
if (snapshot.hasData && snapshot.data) {
return Text("keyboard has focus")
}
return Container();
}),
)
],
),
);
}
}