Flutter:使用 AbsorbPointer 而不重建整个小部件树

时间:2021-07-27 21:15:14

标签: flutter dart setstate

我有一个 Stateful 主页,其中有一个 Stateful widget 子组件的列表。当我点击一个孩子时,我会调用它的 setState() 来向它添加一个 CircularProgressIndicator。这一切都很好。点击一个孩子只会重建那个孩子。

但是,我的主页也包含在 AbsorbPointer 中,并且我想在单击子小部件时设置 absorbing = true。目标是阻止用户在应用程序在后台执行一些异步工作时四处点击。现在的问题是,如果我在主页中调用 setState() 将“吸收”设置为 true,它将重建所有子小部件。

我可以将一些参数传递给子小部件,这样只有我点击的小部件才会有一个 CircularProgressIndicator,但即便如此,所有其他小部件仍将被重建。

我想这归结为这样一个事实,即我无法在不重建所有子级的情况下在父级小部件上调用 setState(),即使我传递给该 setState() (absorbing) 的参数没有任何内容与那些孩子一起做。

是否有解决方法?

谢谢!

// home_screen.dart

class HomeScreen extends StatefulWidget {
  static const String routeName = "homeScreen";
  final MyUser? user;

  const HomeScreen({Key? key, required this.user}) : super(key: key);

  @override
  _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
  final MyDatabase _db = MyDatabase();
  MyUser? _me;

  int _currentPage = -1;
  bool _isLoading = false;

  ...

  @override
  Widget build(BuildContext context) {
    return AbsorbPointer(
      absorbing: _isLoading,
      child: Container(
        decoration: BoxDecoration(
          // color: Color(0xFF0d0717),
          image: DecorationImage(
            image: Image.asset(
              'assets/background.png',
            ).image,
            fit: BoxFit.cover,
          ),
        ),
        child: Scaffold(
          backgroundColor: Colors.transparent,
          appBar: ...,
          bottomNavigationBar: ...,
          body: SingleChildScrollView(
            child: Container(
              padding: const EdgeInsets.symmetric(
                vertical: 8.0,
                horizontal: 12.0,
              ),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  StreamBuilder<QuerySnapshot>(
                    stream: _db.getLiveChannels(),
                    builder: (_, snapshot) {
                      if (!snapshot.hasData) {
                        // print("Has no data");
                        return Center(
                          child: CircularProgressIndicator(),
                        );
                      }
                      _channels.addAll(List.generate(
                          snapshot.data!.docs.length,
                          (index) => Channel.fromSnapshot(
                              snapshot.data!.docs[index])));
                      return Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          Row(
                            children: [
                              Text(
                                'Now Playing',
                                style: TextStyle(
                                  fontSize: 24.0,
                                  fontWeight: FontWeight.w700,
                                ),
                              ),
                              SizedBox(width: 8.0),
                              LiveIndicator(),
                            ],
                          ),
                          SizedBox(height: 8.0),
                          Container(
                            height: 250,
                            child: PageView.builder(
                              physics: PageScrollPhysics(),
                              scrollDirection: Axis.horizontal,
                              controller: PageController(
                                viewportFraction: .9,
                              ),
                              itemCount: _channels.length,
                              itemBuilder: (context, index) {
                                Channel channel =
                                    _channels[_channels.length - 1 - index];
                                return ChildWidget(
                                  callback: _callback;
                                  loading: (_isLoading && _currentPage == index),
                                  key: UniqueKey(),
                                );
                              },
                            ),
                          ),
                          ...,
                        ],
                      );
                    },
                  ),
                  ...,
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }

  Future<void> _callback(params) async {
    if (_isLoading == false) {
      setState(() {
        _isLoading = true;
        _currentPage = index;
      });
    }
    someAsyncMethod().then((_) => setState(() {
          _isLoading = false;
          _currentPage = -1;
        }));
  }

}

// child_widget.dart

class ChildWidget extends StatefulWidget {
  final Future<void> Function(params) callback;
  final bool loading;

  const ChildWidget({
    Key? key,
    required this.callback,
    required this.loading,
  }) : super(key: key);

  @override
  _ChildWidgetState createState() => _ChildWidgetState();
}

class _ChildWidgetState extends State<ChildWidget> {
  late Future<void> Function(params) callback;

  late bool loading;

  @override
  void initState() {
    super.initState();
    callback = widget.callback;
    loading = widget.loading;
  }

  @override
  Widget build(BuildContext context) {
    return Padding(
      child: Column(
        children: [
          Expanded(
            child: CustomClickableWidget(
              onPressed: callback,
              child: Expanded(
                child: Container(
                  child: Stack(
                    children: [
                      ...,
                      if (loading) ...[
                        Container(
                          alignment: Alignment.center,
                          child: CircularProgressIndicator(),
                        ),
                      ],
                    ],
                  ),
                ),
              ),
            ),
          ),
          ...,
        ],
      ),
    );
  }
}

Screenshot

1 个答案:

答案 0 :(得分:0)

SetState 函数会触发 Build() 函数,因此 Build() 函数中存在的所有代码都将再次执行。我不太明白为什么这对你来说是个问题?

另一方面,在您的代码中,我看到您为您的孩子定义了一个键:UniqueKey ()。当 build 函数在 SetState() 之后运行时,它会创建一个新的孩子,而不保留前一个孩子的状态。你不应该在你的函数中定义 UniqueKey () 而是作为你状态的实例变量

ChildWidget(callback: _callback;
            loading: (_isLoading && _currentPage == index),
            key: UniqueKey(),
)

你应该在这里定义你的密钥

class _ChildWidgetState extends State<ChildWidget> {
UniqueKey myKey = UniqueKey();

你的功能

ChildWidget(callback: _callback;
            loading: (_isLoading && _currentPage == index),
            key: myKey,
)