Home > database >  Getting Error Of (Null check operator used on a null value)
Getting Error Of (Null check operator used on a null value)

Time:09-09

I am making a simple shopApp where I implemented the firebase Authentication, AutoLogout and AutoLogin functionality.

The error I am facing is if I restart the app there is no error but once I click on logout button the red screen occurs showing (Null check operator used on a null value).

Here is my main.dart file

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
        providers: [
          ChangeNotifierProvider.value(
            value: Auth(),
          ),
          ChangeNotifierProxyProvider<Auth, ProductsProvider>(
            create: (ctxx) => ProductsProvider('', '', []),
            update: (ctx, auth, previousProduct) => ProductsProvider(
              auth.token,
              auth.userId,
              previousProduct == null ? [] : previousProduct.items,
            ),
          ),
          ChangeNotifierProvider.value(
            value: Cart(),
          ),
          ChangeNotifierProxyProvider<Auth, Order>(
            create: (ctxx) => Order('', '', []),
            update: (ctx, auth, previousProduct) => Order(
                auth.token,
                auth.userId,
                previousProduct == null ? [] : previousProduct.orders),
          ),
        ],
        child: Consumer<Auth>(
          builder: (ctx, auth, _) => MaterialApp(
            title: 'Shop App',
            theme: ThemeData(
              primaryColor: Colors.grey,
              colorScheme:
                  ColorScheme.fromSwatch().copyWith(secondary: Colors.red),
              fontFamily: 'Lato',
            ),
            home: auth.isAuth
                ? const ProductOverviewScreen()
                : FutureBuilder(
                    future: auth.tryAutoLogin(),
                    builder: (ctxx, snapshot) =>
                        snapshot.connectionState == ConnectionState.waiting
                            ? const SplashScreen()
                            : const AuthScreen(),
                  ),
            // initialRoute: '/',
            routes: {
              ProductDescriptionScreen.routName: (context) =>
                  const ProductDescriptionScreen(),
              CartScreen.routName: ((context) => const CartScreen()),
              OrderScreen.orderscreen: (context) => const OrderScreen(),
              UserProductScreen.productitemrout: (context) =>
                  const UserProductScreen(),
              EditProductScreen.routname: (context) =>
                  const EditProductScreen(),
            },
          ),
        ));
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Shop App'),
      ),
      body: const Center(child: Text("My Shop App")),
    );
  }
}

Here is my authentication file

class Auth with ChangeNotifier {
  String? _token;
  DateTime? _expiryDate;
  String? _userId;
  Timer? _authTimer;

  bool get isAuth {
    // return token != null;
    if (_token == null) {
      return false;
    }
    return true;
  }

  String get token {
    if (_expiryDate != null &&
        _expiryDate!.isAfter(DateTime.now()) &&
        _token != null) {
      return _token as String;
    }
    return null as String;
  }

  String get userId {
    return _userId!;
  }

  Future<void> userSignUp(String email, String password) async {
    /* final responce =*/ await http.post(
      Uri.parse(
          'https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=AIzaSyDawXmKNAd-O4EqFGNZWgupIGV-TUWW0Qs'),
      body: json.encode(
        {
          'email': email,
          'password': password,
          'returnSecureToken': true,
        },
      ),
    );
  }

  Future<void> userSignIn(String email, String password) async {
    try {
      final responce = await http.post(
        Uri.parse(
            'https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=AIzaSyDawXmKNAd-O4EqFGNZWgupIGV-TUWW0Qs'),
        body: json.encode(
          {
            'email': email,
            'password': password,
            'returnSecureToken': true,
          },
        ),
      );
      final Responce = json.decode(responce.body);
      if (Responce['error'] != null) {
        throw HttpException(Responce['error']['message']);
      }
      _token = Responce['idToken'];
      _userId = Responce['localId'];
      _expiryDate = DateTime.now().add(
        Duration(
          seconds: int.parse(
            Responce['expiresIn'],
          ),
        ),
      );
      _autoLogOut();
      notifyListeners();
      final prefs = await SharedPreferences.getInstance();
      final userDate = json.encode(
        {
          'token': _token,
          'userId': _userId,
          'expiryDate': _expiryDate!.toIso8601String(),
        },
      );
      prefs.setString('userData', userDate);
    } catch (error) {
      rethrow;
    }
  }

  Future<bool> tryAutoLogin() async {
    final prefs = await SharedPreferences.getInstance();
    if (!prefs.containsKey('userDate')) {
      return false;
    }
    final extractDate =
        json.decode(prefs.getString('userDate')!) as Map<String, Object>;
    final expiryDate = DateTime.parse(extractDate['expiryDate'] as String);
    if (expiryDate.isAfter(DateTime.now())) {
      return false;
    }
    _token = extractDate['token'] as String;
    _userId = extractDate['userId'] as String;
    _expiryDate = expiryDate;
    notifyListeners();
    _autoLogOut();
    return true;
  }

  Future<void> logOut() async {
    _token = null;
    _userId = null;
    _expiryDate = null;
    if (_authTimer != null) {
      _authTimer!.cancel();
    }
    notifyListeners();
    final prefs = await SharedPreferences.getInstance();
    prefs.clear();
  }

  void _autoLogOut() {
    if (_authTimer != null) {
      _authTimer!.cancel();
      _authTimer = null;
    }
    final timeToExpire = _expiryDate!.difference(DateTime.now()).inSeconds;
    _authTimer = Timer(Duration(seconds: timeToExpire), logOut);
  }
}

Here is my AppDrawer Class file

class AppDrawer extends StatelessWidget {
  const AppDrawer({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Drawer(
      child: Column(
        children: <Widget>[
          AppBar(
            title: const Text("Hello Friend"),
            automaticallyImplyLeading: false,
          ),
          const Divider(),
          ListTile(
            leading: const Icon(
              Icons.shop,
            ),
            title: const Text("Shop"),
            onTap: () {
              Navigator.of(context).pushReplacementNamed('/');
            },
          ),
          const Divider(),
          ListTile(
            leading: const Icon(Icons.payment),
            title: const Text("Orders"),
            onTap: () {
              Navigator.of(context)
                  .pushReplacementNamed(OrderScreen.orderscreen);
            },
          ),
          const Divider(),
          ListTile(
            leading: const Icon(Icons.edit),
            title: const Text("Manage Products"),
            onTap: () {
              Navigator.of(context)
                  .pushReplacementNamed(UserProductScreen.productitemrout);
            },
          ),
          const Divider(),
          ListTile(
            leading: const Icon(Icons.exit_to_app),
            title: const Text("LogOut"),
            onTap: () {
              Navigator.of(context).pop();
              Provider.of<Auth>(context, listen: false).logOut();
              //  Navigator.of(context).pushReplacementNamed('/');
            },
          ),
        ],
      ),
    );
  }
}

Here is my Product_itme class file

class ProductItem extends StatelessWidget {
  const ProductItem({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final product = Provider.of<Product>(context);
    final cart = Provider.of<Cart>(context);
    final authData = Provider.of<Auth>(context, listen: false);
    return ClipRRect(
        borderRadius: BorderRadius.circular(15),
        child: GridTile(
          footer: GridTileBar(
            backgroundColor: Colors.black87,
            leading: IconButton(
              icon:
                  Icon(product.isFav ? Icons.favorite : Icons.favorite_border),
              color: Theme.of(context).colorScheme.secondary,
              onPressed: () {
                product.toggleFavStatus(authData.token, authData.userId);
              },
            ),
            title: Text(product.title),
            trailing: IconButton(
              icon: const Icon(Icons.shopping_basket),
              color: Theme.of(context).colorScheme.secondary,
              onPressed: () {
                cart.addProduct(product.id!, product.title, product.price);
                ScaffoldMessenger.of(context).hideCurrentSnackBar();
                ScaffoldMessenger.of(context).showSnackBar(
                  SnackBar(
                    content: const Text(
                      "Item Added To Cart",
                    ),
                    duration: const Duration(seconds: 4),
                    action: SnackBarAction(
                      label: 'Undo',
                      onPressed: () {
                        cart.removeSingleItem(product.id!);
                      },
                    ),
                  ),
                );
              },
            ),
          ),
          child: GestureDetector(
            onTap: () {
              Navigator.of(context).pushNamed(ProductDescriptionScreen.routName,
                  arguments: product.id);
            },
            child: Image.network(
              product.imageUrl,
              fit: BoxFit.cover,
            ),
          ),
        ));
  }
}

Here is my Product_overview_scree where is use the appDrawer

enum FillterdOption {
  favorites,
  show,
}

class ProductOverviewScreen extends StatefulWidget {
  const ProductOverviewScreen({Key? key}) : super(key: key);

  @override
  State<ProductOverviewScreen> createState() => _ProductOverviewScreenState();
}

class _ProductOverviewScreenState extends State<ProductOverviewScreen> {
  var _showOnlyFav = false;
  var _isInIt = true;
  var _isLoaded = false;
  @override
  void didChangeDependencies() {
    if (_isInIt) {
      setState(() {
        _isLoaded = true;
      });
      Provider.of<ProductsProvider>(context).fetchAndSetProduct().then((_) {
        setState(() {
          _isLoaded = false;
        });
      });
    }
    _isInIt = false;
    super.didChangeDependencies();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Product Overview"),
        actions: <Widget>[
          PopupMenuButton(
            onSelected: (FillterdOption value) {
              setState(() {
                if (value == FillterdOption.favorites) {
                  _showOnlyFav = true;
                } else if (value == FillterdOption.show) {
                  _showOnlyFav = false;
                }
              });
            },
            itemBuilder: (_) => [
              const PopupMenuItem(
                value: FillterdOption.favorites,
                child: Text(
                  "Show Favroites",
                ),
              ),
              const PopupMenuItem(
                value: FillterdOption.show,
                child: Text(
                  "Show All",
                ),
              )
            ],
            icon: const Icon(Icons.more_vert),
          ),
          Consumer<Cart>(
            builder: (_, cart, ch) =>
                Badge(value: cart.getCountLenght.toString(), child: ch!),
            child: IconButton(
              icon: const Icon(Icons.shopping_cart),
              onPressed: () {
                Navigator.of(context).pushNamed(CartScreen.routName);
              },
            ),
          )
        ],
      ),
      drawer: const AppDrawer(),
      body: _isLoaded
          ? const Center(
              child: CircularProgressIndicator(),
            )
          : ProductsGrid(showOnlyfac: _showOnlyFav),
    );
  }
}

Here is the error Stack-trace

════════ Exception caught by widgets library ═══════════════════════════════════
The following _CastError was thrown building _InheritedProviderScope<ProductsProvider?>(dirty, dependencies: [_InheritedProviderScope<Auth?>], value: Instance of 'ProductsProvider', listening to value):
type 'Null' is not a subtype of type 'String' in type cast

The relevant error-causing widget was
ChangeNotifierProxyProvider<Auth, ProductsProvider>
package:shop_app/main.dart:31
When the exception was thrown, this was the stack
#0      Auth.token
package:shop_app/provider/auth.dart:28
#1      MyApp.build.<anonymous closure>
package:shop_app/main.dart:34
#2      new ListenableProxyProvider.<anonymous closure>
package:provider/src/listenable_provider.dart:122
#3      _CreateInheritedProviderState.build
package:provider/src/inherited_provider.dart:852
#4      _InheritedProviderScopeElement.build
package:provider/src/inherited_provider.dart:546
#5      ComponentElement.performRebuild
package:flutter/…/widgets/framework.dart:4878
#6      Element.rebuild
package:flutter/…/widgets/framework.dart:4604
#7      BuildOwner.buildScope
package:flutter/…/widgets/framework.dart:2667
#8      WidgetsBinding.drawFrame
package:flutter/…/widgets/binding.dart:882
#9      RendererBinding._handlePersistentFrameCallback
package:flutter/…/rendering/binding.dart:378
#10     SchedulerBinding._invokeFrameCallback
package:flutter/…/scheduler/binding.dart:1175
#11     SchedulerBinding.handleDrawFrame
package:flutter/…/scheduler/binding.dart:1104
#12     SchedulerBinding._handleDrawFrame
package:flutter/…/scheduler/binding.dart:1015
#13     _invoke (dart:ui/hooks.dart:148:13)
#14     PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:318:5)
#15     _drawFrame (dart:ui/hooks.dart:115:31)
════════════════════════════════════════════════════════════════════════════════
Application finished.
Exited

CodePudding user response:

The problem is the folowing line:

return null as String;

null cannot be converted to String with the cast operator as. I'd recommend changing it to return an empty String instead. Something like the below code snippet:

String get token {
  if (_expiryDate != null &&
      _expiryDate!.isAfter(DateTime.now()) &&
      _token != null) {
    return _token!;
  }
  return '';
}

I'd also change the ChangeNotifierProxyProvider<Auth, ProductsProvider> to check if there's some auth in place before constructing a new ProductsProvider (The same for ChangeNotifierProxyProvider<Auth, Order> for the Order). Something like the below code snippet:

ChangeNotifierProxyProvider<Auth, ProductsProvider>(
  create: (ctxx) => ProductsProvider('', '', []),
  update: (ctx, auth, previousProduct) => auth.isAuth
      ? ProductsProvider(
          auth.token,
          auth.userId,
          previousProduct == null ? [] : previousProduct.items,
        )
      : previousProduct,
),

ChangeNotifierProxyProvider<Auth, Order>(
  create: (ctxx) => Order('', '', []),
  update: (ctx, auth, previousOrder) => auth.isAuth
      ? Order(auth.token, auth.userId,
          previousOrder == null ? [] : previousOrder.orders)
      : previousOrder,
),
  • Related