Flutter Navigator.pop 再次运行小部件构建方法

Posted

技术标签:

【中文标题】Flutter Navigator.pop 再次运行小部件构建方法【英文标题】:Flutter Navigator.pop running widget build method again 【发布时间】:2020-12-30 01:31:15 【问题描述】:

我有两个小部件

    类别小部件 - 我在其中显示类别的名称和图像 CategoryDe​​tailsS​​creen 小部件 - 我在其中显示与该类别相关的项目列表

我在类别小部件上有 onTap 方法,从中我将一些路由参数传递到详细信息屏幕

onTap: () 
    Navigator.of(context).pushNamed(
      CategoryDetailScreen.routeName,
      arguments: 
        'id': id,
        'name': name,
        'slug': slug,
      ,
    );
  

在类别详细信息页面上,我得到了这些参数

final categoryArgs =
    ModalRoute.of(context).settings.arguments as Map<String, String>;

现在,当我尝试使用应用栏后退按钮返回时,它会再次调用我的类别详细信息屏幕的构建方法。然后它再次调用我的 API。我想避免这种情况,并且不想在返回类别页面时再次调用我的 API。

这是我的两个小部件的完整代码 -

category.dart

import 'package:flutter/material.dart';
import 'dart:ui';
import 'package:cached_network_image/cached_network_image.dart';

import 'package:captionsocial/screens/category_detail_screen.dart';

class Category extends StatelessWidget 
  final String id;
  final String name;
  final String image;
  final String slug;

  Category(this.id, this.name, this.image, this.slug);

  @override
  Widget build(BuildContext context) 
    return InkWell(
      onTap: () 
        Navigator.of(context).pushNamed(
          CategoryDetailScreen.routeName,
          arguments: 
            'id': id,
            'name': name,
            'slug': slug,
          ,
        );
      ,
      borderRadius: BorderRadius.circular(12),
      child: Container(
        decoration: BoxDecoration(
          image: DecorationImage(
            alignment: Alignment.center,
            fit: BoxFit.cover,
            image: CachedNetworkImageProvider(
              image,
            ),
          ),
          borderRadius: BorderRadius.circular(12),
        ),
        child: ClipRRect(
          borderRadius: BorderRadius.circular(12),
          child: BackdropFilter(
            filter: ImageFilter.blur(sigmaX: 0, sigmaY: 0),
            child: Container(
              alignment: Alignment.center,
              color: Colors.black.withOpacity(0.5),
              child: Text(
                name,
                textAlign: TextAlign.center,
                style: TextStyle(
                  color: Colors.white,
                  fontSize: 20,
                  fontWeight: FontWeight.bold,
                ),
              ),
            ),
          ),
        ),
      ),
    );
  


类别详情屏幕

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

import '../providers/posts.dart';

class CategoryDetailScreen extends StatelessWidget 
  static const routeName = 'category-detail';

  @override
  Widget build(BuildContext context) 
    print('running again');
    final categoryArgs =
        ModalRoute.of(context).settings.arguments as Map<String, String>;
    final productsData = Provider.of<Posts>(context, listen: false);
    final temp = productsData.getPostsBySlug(categoryArgs['slug']);

    return Scaffold(
      backgroundColor: Colors.white,
      appBar: AppBar(
        leading: IconButton(
            icon: Icon(Icons.arrow_back_ios),
            onPressed: () 
              Navigator.pop(context);
            ),
        elevation: 1,
        backgroundColor: Colors.white,
        title: Text(
          categoryArgs['name'],
          style: TextStyle(color: Theme.of(context).primaryColor),
        ),
      ),
      body: FutureBuilder(
        future: temp,
        builder: (ctx, snapShot) =>
            snapShot.connectionState == ConnectionState.waiting
                ? Center(
                    child: CircularProgressIndicator(),
                  )
                : Consumer<Posts>(
                    builder: (ct, postsData, child) => Padding(
                      padding: EdgeInsets.symmetric(horizontal: 0, vertical: 0),
                      child: ListView.builder(
                        padding: const EdgeInsets.all(10.0),
                        itemCount: postsData.posts.length,
                        itemBuilder: (ctx, i) => Text(postsData.posts[i].title),
                      ),
                    ),
                  ),
      ),
    );
  

导致问题的行是

final categoryArgs =
    ModalRoute.of(context).settings.arguments as Map<String, String>;

如果我删除上面的行,那么当我使用 appbar 后退按钮返回时不会再次调用 build 方法。

【问题讨论】:

【参考方案1】:

您需要在此处使用StatefulWidget。据我所知,你无法控制flutter何时调用小部件的build方法,即使是无状态的也是如此。

将涉及您的Future 的部分移动到StatefulWidgetState 中的initStateinitState 可能无法用于这种情况,因为需要 BuildContext,因此如果这导致错误,请将其移至 didChangeDependencies

class CategoryDetailScreen extends StatefulWidget 
  @override
  _CategoryDetailScreenState createState() => _CategoryDetailScreenState();


class _CategoryDetailScreenState extends State<CategoryDetailScreen> 
  var temp;

  @override
  void initState() 
    super.initState();
    final categoryArgs =
        ModalRoute.of(context).settings.arguments as Map<String, String>;
    final productsData = Provider.of<Posts>(context, listen: false);
    temp = productsData.getPostsBySlug(categoryArgs['slug']);
  

【讨论】:

以上是关于Flutter Navigator.pop 再次运行小部件构建方法的主要内容,如果未能解决你的问题,请参考以下文章

Navigator.pop(context) 不返回上一屏 Flutter

如何在 Flutter 中的 navigator.pop(context) 之后显示小吃栏?

Flutter BLoC:构建()方法中 StreamBuilder 中的 Navigator.pop

Flutter - 从 AppBar 返回上一页不会刷新页面,使用 Navigator.pop(context)

Flutter Navigator.pop() 保持对话框部分可见,黑色阴影背景

调用 Navigator Pop 或 Push 时触发 Flutter Stream Builder