弹出后提供者为空

Provider null after pop

我正在尝试使用具有 MVVM 架构的 Provider,我在 provider 上有一个非常奇怪的错误,在主页上有 3 个部分,横幅,折扣和类别,当我更改折扣中的商品数量时,一切正常,但是切换到一个类别并返回后,当我更改商品数量时它已经给出了错误, 数据不为空,我认为是provider的问题,方案如下:

discount section-> quantity of goods -> works

home-> category-> go back-> quantity of goods in discounts-> not working

演示 project

The following _CastError was thrown building MainPage(dirty, dependencies: [_InheritedProviderScope<AllGoodsViewModel?>, _LocalizationsScope-[GlobalKey#a9792], MediaQuery, _InheritedProviderScope<MainPageListViewModel?>], state: _MainPageState#eb568):
Null check operator used on a null value

The relevant error-causing widget was: 
  MainPage MainPage:file:///Users/.../lib/main.dart:93:21
When the exception was thrown, this was the stack: 
#0      Element.widget (package:flutter/src/widgets/framework.dart:3229:31)
#1      debugCheckHasMediaQuery.<anonymous closure> (package:flutter/src/widgets/debug.dart:245:17)
#2      debugCheckHasMediaQuery (package:flutter/src/widgets/debug.dart:261:4)
#3      MediaQuery.of (package:flutter/src/widgets/media_query.dart:908:12)
#4      ScreenUtil.screenWidth (package:flutter_screenutil/src/screen_util.dart:148:37)
#5      ScreenUtil.scaleWidth (package:flutter_screenutil/src/screen_util.dart:167:28)
#6      ScreenUtil.setWidth (package:flutter_screenutil/src/screen_util.dart:182:41)
#7      SizeExtension.w (package:flutter_screenutil/src/size_extension.dart:9:32)
#8      _MainPageState.build (package:.../View/MainPage.dart:240:78)
#9      StatefulElement.build (package:flutter/src/widgets/framework.dart:4919:27)
#10     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4806:15)
#11     StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4977:11)
#12     Element.rebuild (package:flutter/src/widgets/framework.dart:4529:5)
#13     BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2659:19)
#14     WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:891:21)
#15     RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:370:5)
#16     SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1146:15)
#17     SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1083:9)
#18     SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:997:5)
#22     _invoke (dart:ui/hooks.dart:151:10)
#23     PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:308:5)
#24     _drawFrame (dart:ui/hooks.dart:115:31)
(elided 3 frames from dart:async)

ScreenUtilInitService

class ScreenUtilInitService z{
  /// A helper widget that initializes [ScreenUtil]
  ScreenUtilInitService({required this.builder, Key? key,}) : super(key: key);

  final Widget Function(BuildContext) builder;

  @override
  Widget build(BuildContext context) {
    return  ScreenUtilInit(
        designSize: Size(375, 812),
        builder: (context, widget) => builder(context)
    );
  }
}
class MyApp extends StatefulWidget {

  @override
  State<MyApp> createState() => _MyAppState();

}

class _MyAppState extends State<MyApp> {

  bool _initialized = false;
  bool _error = false;
  Widget _present = SplashScreen();

  void initializeFlutterFire() async {
    try {
      if (Platform.isAndroid) {
        await AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true);
      }
      WidgetsFlutterBinding.ensureInitialized();
      await Firebase.initializeApp();
      await message();
      await rootWidget();
      setState(() {
        _initialized = true;
      });
    } catch(e) {
      setState(() {
        _error = true;
      });
    }
  }

  Future<void> rootWidget() async {
    final prefs = await SharedPreferences.getInstance();
    final id = prefs.get("idAddress");
    final logged = prefs.getBool("logged") ?? false;
    if ((FirebaseAuth.instance.currentUser?.uid != null && logged) || id != null) {
      setState(() {
        _present =  MainPage();
      });
      return;
    } else {
      setState(() {
        _present = OfferPage();
      });
      return;
    }
  }

  @override
  void initState() {
    initializeDB();
    initializeFlutterFire();
    super.initState();
    listeners();
  }

  @override
  Widget build(BuildContext context) {
    if(_error) {
      return MaterialApp(home:SplashScreen());
    }
    if (!_initialized) {
      return MaterialApp(home:SplashScreen());
    }
    return RootPage().mainPage(present: _present);
  }
}

和 RootPage

class RootPage {
  

  Widget mainPage({Widget? present}){
    return MaterialApp(
        initialRoute: '/',
        routes: {
          '/ProfilePage': (context) => ProfilePage(),
          '/MainPage': (context) => MainPage(),
          '/CartPage': (context) => CartPage(),
        },
        builder: (context, widget) {
          return ScreenUtilInitService(
              builder: (context) => widget!
          );
        },
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: MultiProvider(
          providers: [
            ChangeNotifierProvider(
              create: (context) => MainPageListViewModel(),
            ),
            ChangeNotifierProvider(
                create: (context) => CartViewModel(),
                child: CartPage()
            ),
            ChangeNotifierProvider(
              create: (context) => AllGoodsViewModel(),
            ),
            ChangeNotifierProvider(
              create: (context) => GoodsViewModel(),
            ),
          ],
          child: present != null ? present : MainPage(),
        ),
    );
  }

}

主页

  @override
  void initState() {
    setting();
    super.initState();
    _scrollListener();
    _notification();
  }

  @override
  void dispose() {
    _scrollController.removeListener(() { });
    _scrollController.dispose();
    NotificationCenter().unsubscribe('cart');
    NotificationCenter().unsubscribe('address');
    super.dispose();
  }

  void setting() async {
    final cart = await SQFliteService.cart.getCount();
    final address = await SQFliteService.location.current();
    setState((){
      _address = address;
      showCart = cart == 0 ? false : true;
    });
    Provider.of<MainPageListViewModel>(context, listen: false).deliveryRequest() ;
    Provider.of<MainPageListViewModel>(context, listen: false).fetchBanner();
    Provider.of<MainPageListViewModel>(context, listen: false).fetchCategory();
    Provider.of<AllGoodsViewModel>(context, listen: false).fetchSale();
    Provider.of<MainPageListViewModel>(context, listen: false).fetchLaunchMessage(context);

  }

  void _scrollListener(){
    _scrollController.addListener(() {

    ///setState is null after pop after category
    //   setState(() {
    //  _bottomOffSet = _scrollController.offset;
    //   });
    });
  }

  void _notification(){

    NotificationCenter().subscribe('cart', () async {
      final result = await SQFliteService.cart.getCount();
      setState(() {
        showCart = result == 0 ? false : true;
        print("update");
      });
    });

    NotificationCenter().subscribe('address', () async {
      final result = await SQFliteService.location.current();
      setState((){
        _address = result;
      });
    });

  }

  @override
  Widget build(BuildContext context) {

    final models = Provider.of<MainPageListViewModel>(context);
    final sale = Provider.of<AllGoodsViewModel>(context);

    final size = MediaQuery.of(context).size;

return Container(
      child: CustomScrollView(
        physics: AlwaysScrollableScrollPhysics(),
        controller: _scrollController,
        slivers: [
          SliverAppBar(
            ...
          ),
          SliverList(
            delegate: SliverChildBuilderDelegate((context, index) => Container(
              color: Colors.transparent,
              child: Column(
                  children: [
                    bannerW(),
                    ViewSale(model: sale.goods),
                    SizedBox(height: 10),
                    ViewCategory(model: models.category)
                  ]
              ),
            ),
                childCount: 1
            ),
          ),
        ],
      ),
    );

ViewSale

class _ViewSaleState extends State<ViewSale> {
  @override
  Widget build(BuildContext context) {
    return Column(
        children: [
          Container(
            margin: EdgeInsets.only(left: 16, right: 16, bottom: 15),
            child: Align(
                alignment: Alignment.centerLeft,
                child: Text("Скидки", style: TextStyle(fontFamily: "Inter", fontWeight: FontWeight.w900, fontSize: 18))),
          ),
          Container(
            height: ((MediaQuery.of(context).size.width/2.4) - 21) + 71,
            width: MediaQuery.of(context).size.width,
            child: ListView.builder(
                padding: EdgeInsets.symmetric(horizontal: 16),
                shrinkWrap: true,
                itemCount: widget.model.length,
                scrollDirection: Axis.horizontal,
                itemBuilder: (item, index) {
                  return ChangeNotifierProvider(
                     create: (context) => AllGoodsViewModel(),
                    child: ViewGoodsSale(model: widget.model[index])
                   );
                }),
          )
        ]
    );
  }
}

查看销售商品

class _ViewGoodsSaleState extends State<ViewGoodsSale> {

  GlobalKey _key = GlobalKey();

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_){
      Provider.of<AllGoodsViewModel>(context, listen: false).setting(widget.model);
    });
  }

  @override
  Widget build(BuildContext context) {

    final model = Provider.of<AllGoodsViewModel>(context);
    final size = MediaQuery.of(context).size;

    Widget inCart(){
    return Container(
      key: _key,
      height: 31,
      child: GestureDetector(
        onPanDown: (details) {
          Goods? item = widget.model;
          RenderBox _cardBox = _key.currentContext!.findRenderObject() as RenderBox;
          final localPosition = details.localPosition;
          final localDx = localPosition.dx;
          if (localDx <= _cardBox.size.width/2) {
            Goods value = cart.firstWhere((element) => element.id == item.id);
            if (item.optState == 0 ? value.orderCount <= 1 : value.orderCount <= value.opt!.count) {
              setState(() {
                context.read<AllGoodsViewModel>().setCountInCart(0);
                final ind = cart.indexWhere((element) => element.id == item.id);
                if (ind != -1) {
                  cart[ind].orderCount = 0;
                  SQFliteService.cart.delete(cart[ind].id);
                  cart.removeAt(ind);
                }
              });
            } else {
              model.haveItem(item: item, operation: item.optState == 0 ? -1 : (-1 * value.opt!.count));
            }
          } else {
            model.haveItem(item: item, operation: item.optState == 0 ? 1 : item.count);
          }
        },
        child: TextButton(
          style: ButtonStyle(
              backgroundColor: MaterialStateProperty.all(Design.appColor),
              padding: MaterialStateProperty.all(EdgeInsets.symmetric(vertical: 8, horizontal: 10)),
              shape: MaterialStateProperty.all(RoundedRectangleBorder(
                borderRadius: BorderRadius.circular(10.0),
              ))
          ),
          onPressed: (){},
          child: Container(
            child: RichText(
              text:  TextSpan(
                text: "",
                children:[
                  WidgetSpan(
                    alignment: PlaceholderAlignment.middle,
                    child: Icon(Icons.remove, size: 14, color: Colors.white),
                  ),
                  TextSpan(
                    text: "  ${widget.model.optState == 0 ? (widget.model.minPrice ?? widget.model.price) : widget.model.opt!.price} ₽  ",
                    style: TextStyle(
                        color: Colors.white,
                        fontSize: 14,
                        fontWeight: FontWeight.w500,
                        fontFamily: "Inter"
                    ),
                  ),
                  WidgetSpan(
                    alignment: PlaceholderAlignment.middle,
                    child: Icon(Icons.add, size: 14, color: Colors.white),
                  )
                ],
              ),
            ),
          ),
        ),
      ),// Your TextButton code goes here.
    );
  }

  Widget noInCart(){
    return Container(
      key: _key,
      height: 31,
      child: TextButton(
        style: ButtonStyle(
            backgroundColor: MaterialStateProperty.all(model.orderBg),
            padding: MaterialStateProperty.all(EdgeInsets.symmetric(vertical: 8, horizontal: 10)),
            shape: MaterialStateProperty.all(RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(10.0),
            ))
        ),
        onPressed: (){
          Goods? item = widget.model;
          model.haveItem(item: item, operation: item.optState == 0 ? 1 : item.count);
        },
        child: Container(
          child: RichText(
            text:  TextSpan(
              text: "${widget.model.optState == 0 ? widget.model.minPrice == null ? widget.model.price : widget.model.minPrice : widget.model.opt!.price} ₽ ",
              style: TextStyle(
                  color: widget.model.minPrice != null ? Design.grey : Colors.black,
                  decoration: widget.model.optState == 0 && widget.model.minPrice != null ? TextDecoration.lineThrough : TextDecoration.none,
                  fontSize: 14,
                  fontWeight: FontWeight.w500,
                  fontFamily: "Inter"

              ),
              children:[
                TextSpan(
                  text: widget.model.minPrice == null ? "" : " ${widget.model.price} ₽",
                  style: TextStyle(
                      color: Colors.black,
                      decoration: TextDecoration.none,
                      fontSize: 14,
                      fontWeight: FontWeight.w500,
                      fontFamily: "Inter"
                  ),
                ),

                WidgetSpan(
                  alignment: PlaceholderAlignment.middle,
                  child: Icon(Icons.add, size: 14, color: Colors.black),
                  style: TextStyle(
                    color: Colors.black,
                    decoration: TextDecoration.none,
                  ),
                )
              ],
            ),
          ),
        ),
      ),
    );
  }


  Widget card({required Size size}) {
    return Container(
      color: Colors.white,
      width: (size.width/2.4) - 11,
      margin: EdgeInsets.only(right: 10),
      child: Column(
        children: [
          Stack(
              children: [
                Container(
                  height: (size.width/2.4) - 21,
                  padding:  EdgeInsets.all(1),
                  decoration: BoxDecoration(
                    image: DecorationImage(
                        fit: BoxFit.contain,
                        image: NetworkImage(widget.model.images.first)
                    ),
                    borderRadius: BorderRadius.all(Radius.circular(10.0)),
                    border: Border.all(
                        width: 1,
                        color: Design.lightGrey
                    ),
                  ),
                ),
                Visibility(
                  visible: context.read<AllGoodsViewModel>().countInCart == 0 ? false : true,
                  child: Container(
                    height: (size.width/2.4) - 21,
                    decoration: BoxDecoration(
                      color: Colors.black.withOpacity(0.5),
                      borderRadius: BorderRadius.all(Radius.circular(10.0)),
                    ),
                    child: Visibility(
                      visible: true,
                      child: Center(
                        child: model.orderCountText,
                      ),
                    ),
                  ),
                )
              ]
          ),
          SizedBox(height: 5),
          Align(
            alignment: Alignment.centerLeft,
            child: Container(
              height: 29,
              child: Text(widget.model.name,
                maxLines: 2,
                style: TextStyle(
                  fontSize: 12,
                  fontWeight: FontWeight.w700,
                  fontFamily: "Inter",
                ),
              ),
            ),
          ),
          SizedBox(height: 6),
          Align(
              alignment: Alignment.centerLeft,
              child: (context.read<AllGoodsViewModel>().countInCart == 0) ? noInCart() : inCart()
          )
        ],
      ),
    );
  }

  return card(size: size);
  }

}

查看类别

class _ViewCategoryState extends State<ViewCategory> {

  @override
  Widget build(BuildContext context) {
    return Container(
      child: GridView.builder(
        padding: EdgeInsets.all(16),
        shrinkWrap: true,
          physics: NeverScrollableScrollPhysics(),
          gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
              crossAxisCount: 2,
              childAspectRatio: 1),
        itemCount: widget.model.length,
        itemBuilder: (context, index) {
          return GestureDetector(
              child: Stack(
                children: [
                  Container(
                    decoration: BoxDecoration(
                      color: Design.lightGrey,
                      borderRadius: BorderRadius.all(Radius.circular(10.0)),
                    ),
                      margin: EdgeInsets.only(right: (index.isOdd ? 0 : 5) , left: (index.isOdd ? 5 : 0), bottom: 10 ),
                      child: Image.network(widget.model[index].category?.url ?? "")
                  ),
                  Positioned(
                      left: 10,
                      right: 10,
                      top: 8,
                      child: Text(widget.model[index].category?.name ?? "", style: TextStyle(fontFamily: "Inter", fontWeight: FontWeight.w500, fontSize: 12.sp)),
                  )
                ]
              ),
              onTap: () async {
                if (widget.model[index].category != null) {
                  final data = widget.model[index].category!;
                  final category = CategoryData(id: data.id, name: data.name);
                  if (data.tags == null) {
                    Navigator.of(context).push(
                        MaterialPageRoute(builder: (context) =>
                            ChangeNotifierProvider.value(value: AllGoodsViewModel(),
                              child: AllCategoryGoodsPage(category: category))
                        )
                    );
                  } else {
                    Navigator.of(context).push(
                        MaterialPageRoute(builder: (context) =>
                            ChangeNotifierProvider.value(value: GoodsViewModel(),
                                child: GoodsPage(category: category))
                        )
                    );
                  }
                  FirebaseAnalytics.instance.logEvent(name: "Category", parameters: null);
                }
              }
            );
          },
      ),
    );
  }
}

"Give a man a fish, feed him for a day. Teach a man to fish, feed him for a lifetime."

因此,我尝试解释一般情况下该做什么。

首先,查看错误堆栈跟踪。您只提供错误 message 而没有 stacktrace - 因此难怪没人能看到哪一行代码引发了错误。

P.S。如果找不到堆栈跟踪,请搜索或创建一个单独的问题(在某些奇怪的情况下堆栈跟踪不容易找到)。

当您获得堆栈跟踪时,您可以了解哪行代码有此空指针错误,以及哪些行正在调用该函数。假设它是 a.dart123 抛出错误,然后查看该行并检查为什么会发生这种情况。

您可能想要进行“记录”或“调试”以挖掘更多线索。

那么错误应该很容易找到并解决。如果没有,请尝试创建一个 最小 可重现样本 - 您当前的样本仍然很大,人们很难为您挖掘和发现错误 :)

"空值检查运算符 " 是在未初始化的可空实例上使用 bang 运算符 (!) 时出现的错误。

在您的“ViewSaleGoods”文件中,它位于 -

    if (item.optState == 0 ? value.orderCount <= 1 : value.orderCount <= value.opt!.count) { // Here .....
                  setState(() {
                    context.read<AllGoodsViewModel>().setCountInCart(0);
                    final ind = cart.indexWhere((element) => element.id == item.id);
                    if (ind != -1) {
                      cart[ind].orderCount = 0;
                      SQFliteService.cart.delete(cart[ind].id);
                      cart.removeAt(ind);
                    }
                  });
                } else {
              model.haveItem(item: item, operation: item.optState == 0 ? -1 : (-1 * value.opt!.count)); // here...
            }
    
                TextSpan(
                        text: "  ${widget.model.optState == 0 ? (widget.model.minPrice ?? widget.model.price) : widget.model.opt!.price} ₽  ", // and here ....
                        style: TextStyle(
                            color: Colors.white,
                            fontSize: 14,
                            fontWeight: FontWeight.w500,
                            fontFamily: "Inter"
                        ),
                      ),

所以这些人中的任何一个都变成了 null,所以错误。现在要修复它,您可以使用一些方法,例如 -

    // Use variable and if check
    var nullSafe;
    if (value.opt != null) {
        nullSafe = value.opt; // now you can use these variable instead
    }

    // Or use ?. and ??
    value.opt?count ?? "some default value"

希望对您有所帮助。

您的 IDE 通常会有很好的堆栈跟踪调试。如果您正在使用 vscode,您可以为错误跟踪打开以下两个选项。

如果您使用的是 Android Studio。 运行 -> 查看断点。

我解决了问题,错误根本不是在provider,而是在ScreenUtilInitService我改成了

class ScreenUtilInitService {
  Widget build(Function(BuildContext) builder) {
    return ScreenUtilInit(
        designSize: Size(375, 812),
        builder: (context, widget) => builder(context)
    );
  }
}

并从 GoodsPageAllCategoryGoodsPage 中删除了 ScreenUtilInitService,因此 ScreenUtilInitService 未被重新定义为小部件,这导致了错误