skip to Main Content

Device returns "Null check operator used on a null value" exception for a second, however i added checker to make sure value is not null. I am new in Flutter and i know this code is horrible, so can anyone gave some advices to make it more cleaner?

class _HotelPageState extends State<HotelPage> {
  HotelMode? hotmod;                              // object of class
  var isLoaded = false;

  @override
  void initState() {
    super.initState();
    getData();
  }

  getData() async {
    hotmod = await HotelService().getHotelInfo();
    if (hotmod != null) {                            // checks if it has data
      setState(() {
        isLoaded = true;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        backgroundColor: backgroundbgtheme,
        appBar: AppBar(
          centerTitle: true,
          title: const Text(
            'Отель',
            style: TextStyle(color: Colors.black),
          ),
          toolbarHeight: 50,
          backgroundColor: backgroundtheme,
          elevation: 0,
        ),
        body: Visibility(
          replacement: const Center(
            child: CircularProgressIndicator(),
          ),
          visible: isLoaded,
          child: ListView(
            children: [ HotelScreen(
              image: hotmod!.imageUrls[1],
                hotelname: hotmod!.name,
                price: hotmod!.minimalPrice,
                priceForIt: hotmod!.priceForIt,
                country: hotmod!.adress,
                rating: hotmod!.rating,
                ratingName: hotmod!.ratingName),
              HotelDescription(hotdesc: hotmod!.aboutTheHotel.description),
              const BottomButtonWidget()
              ]
          ),
        )
    );
  }
}

3

Answers


  1. You can handle loading and error for future methods with FutureBuilder.

    Create a Future method with return type HotelMode:

    Future<HotelMode> futureMethod() async {
      HotelMode hotmod = await HotelService().getHotelInfo();
      return hotmod;
    }
    

    Use FutureBuilder for scaffold’s body:

    FutureBuilder(
        future: futureMethod(),
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.waiting) {
            return const Center(
              child: CircularProgressIndicator(),
            );
          } else if (snapshot.hasError) {
            return Center(child: Text('Error: ${snapshot.error}'));
          } else if (snapshot.hasData) {
            HotelMode hotmod = snapshot.data!;
            return YourListViewWidget();
          } else {
            return const Text('No data available');
          }
        },
      )
    
    Login or Signup to reply.
  2. the data you’re getting (** getHotelInfo**) is in Future. And UI will not wait until data comes.You should use FutureBuilder in this case. or make all parametrs of HotelScreen widget nullable. Here is how you can use FutureBuilder in your case:

    class HotelPage extends StatefulWidget {
      const HotelPage({super.key});
    
      @override
      State<HotelPage> createState() => _HotelPageState();
    }
    
    class _HotelPageState extends State<HotelPage> {
      
       Future getData ()async{
         return await HotelService().getHotelInfo();
        
        }
      
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
            backgroundColor: backgroundbgtheme,
            appBar: AppBar(
              centerTitle: true,
              title: const Text(
                'Отель',
                style: TextStyle(color: Colors.black),
              ),
              toolbarHeight: 50,
              backgroundColor: backgroundtheme,
              elevation: 0,
            ),
            body: FutureBuilder(
            future:getData(),
             builder: (BuildContext context, AsyncSnapshot snapshot) {
               if(snapshot.hasData){
                 var hotmod = snapshot.data;
               return ListView(
                children: [ HotelScreen(
                  image: hotmod!.imageUrls[1],
                    hotelname: hotmod!.name,
                    price: hotmod!.minimalPrice,
                    priceForIt: hotmod!.priceForIt,
                    country: hotmod!.adress,
                    rating: hotmod!.rating,
                    ratingName: hotmod!.ratingName),
                    HotelDescription(hotdesc: hotmod!.aboutTheHotel.description),
                   const BottomButtonWidget(),
                  ]
              );
               }else if (snapshot.hasError){
                 return Text('Something happened');
               }else{
                 return  Center(
                child: CircularProgressIndicator(),
              );
               }
             }
            )
          
        
        );
          
      }
    }
    
    Login or Signup to reply.
  3. Your problem is, that the initState method is not waiting on the result of getData. It runs, getData is not done yet, the widget it build the first time and crashes, because for a specific time, hotmod is null. Only later it is filled.

    So at least you need to make sure your build method does not crash when hotmod is null. Right now, it is invisible, but it still is build and crashes. So don’t build it if it is null:

    class _HotelPageState extends State<HotelPage> {
      HotelMode? hotmod;                              // object of class
      var isLoaded = false;
    
      @override
      void initState() {
        super.initState();
        getData();
      }
    
      getData() async {
        hotmod = await HotelService().getHotelInfo();
        if (hotmod != null) {                            // checks if it has data
          setState(() {
            isLoaded = true;
          });
        }
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
            backgroundColor: backgroundbgtheme,
            appBar: AppBar(
              centerTitle: true,
              title: const Text(
                'Отель',
                style: TextStyle(color: Colors.black),
              ),
              toolbarHeight: 50,
              backgroundColor: backgroundtheme,
              elevation: 0,
            ),
            body: 
              !isLoaded ?
              const Center(
                child: CircularProgressIndicator(),
              )
              : 
              ListView(
                children: [ HotelScreen(
                  image: hotmod!.imageUrls[1],
                    hotelname: hotmod!.name,
                    price: hotmod!.minimalPrice,
                    priceForIt: hotmod!.priceForIt,
                    country: hotmod!.adress,
                    rating: hotmod!.rating,
                    ratingName: hotmod!.ratingName),
                  HotelDescription(hotdesc: hotmod!.aboutTheHotel.description),
                  const BottomButtonWidget()
                  ]
              ),
            )
        );
      }
    }
    

    However, there is a solution already ready-made for this in flutter:

    See the example at What is a Future and how do I use it?

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search