Home > Net >  Refactoring switch case with connection state
Refactoring switch case with connection state

Time:06-12

i'm using switch case inside future builder (below) with connection state to view certain widgets in different connection states.

My question is, how can I refactor the switch case so i can use it in other places in the app?

Thanks..

code:

FutureBuilder(
  future: _fetchProductsList,
  builder: (context, AsyncSnapshot<List<ProductsList>> snapshot) {
    switch(snapshot.connectionState){
      case ConnectionState.none:
        return const Text("There is no connection!!");

      case ConnectionState.active:
      case ConnectionState.waiting:
        return const Center(child: CircularProgressIndicator());
      
      case ConnectionState.done:
      if(snapshot.hasData){
        if (snapshot.data!.isNotEmpty){

          List<ProductsList>? product = snapshot.data;

          return ListView.builder(
            primary: false,
            shrinkWrap: true,
            scrollDirection: Axis.horizontal,
            itemCount: product!.length,
            itemBuilder: (BuildContext context,int index){
              return ProductCard(
                img: product[index].image,
                title: product[index].title,
                price: product[index].price,
                rating: product[index].rating.rate,
                discount: 50,
                onTap: () {}
              );
            }
          );
        }else {
          // here your snapshot data is null, has no data...
          return const Center(
            child: Text(
              "No Data Available Right Now!!"
            )
          );
        }
      }
    }
    // By default, show a loading spinner
    return const Center(child: CircularProgressIndicator());
  }
)

CodePudding user response:

You can extract the whole FutureBuilder in a separate widget with a Generic Type to handle any type of data returned by the future.

For example I made a widget called CustomFutureBuilder like the following:

class CustomFutureBuilder<T> extends StatelessWidget {
  final Future<T> future;
  final Function(T data) successBuilder;

  const CustomFutureBuilder({
    Key? key,
    required this.future,
    required this.successBuilder,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<T>(
      future: future,
      builder: (context, AsyncSnapshot<T> snapshot) {
        switch (snapshot.connectionState) {
          case ConnectionState.none:
            return const Text("There is no connection!!");

          case ConnectionState.active:
          case ConnectionState.waiting:
            return const Center(child: CircularProgressIndicator());

          case ConnectionState.done:
            if (snapshot.hasData) {
              successBuilder(snapshot.data!);
            }
        }
        // By default, show a loading spinner
        return const Center(child: CircularProgressIndicator());
      },
    );
  }
}

Here I made a trick by passing a custom builder method that receives the data after all the checks, you can also pass any number of parameters you want to customize everything.

Usage Example:

CustomFutureBuilder<List>(
  future: _fetchProductsList,
  successBuilder: (data) {
    if (data.isNotEmpty) {
      List<ProductsList>? product = snapshot.data;

      return ListView.builder(
        primary: false,
        shrinkWrap: true,
        scrollDirection: Axis.horizontal,
        itemCount: product!.length,
        itemBuilder: (BuildContext context, int index) {
          return ProductCard(
              img: product[index].image,
              title: product[index].title,
              price: product[index].price,
              rating: product[index].rating.rate,
              discount: 50,
              onTap: () {});
        },
      );
    } else {
      // here your snapshot data is null, has no data...
      return const Center(child: Text("No Data Available Right Now!!"));
    }
  },
);

CodePudding user response:

Try this demo:

future.dart file:

import 'package:flutter/material.dart';
abstract class FutureBuilderRequest extends StatelessWidget{
  const FutureBuilderRequest({Key? key});

  Widget hasDataWidget(dynamic data);
  Widget noDataWidget();
  Widget noneConnectionWidget();
  Widget activeWidget();
  Widget waitingWidget();
  Future future();

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
      future: future(),
      builder: (context, AsyncSnapshot<dynamic> snapshot) {
        switch (snapshot.connectionState) {
          case ConnectionState.none:
            return noneConnectionWidget();
          case ConnectionState.active:
            return noneConnectionWidget();
          case ConnectionState.waiting:
            return waitingWidget();

          case ConnectionState.done:
            if (snapshot.hasData) {
              if (snapshot.data!.isNotEmpty) {
                return hasDataWidget(snapshot.data);
              } else {
                // here your snapshot data is null, has no data...
                return noDataWidget();
              }
            }
        }
        // By default, show a loading spinner
        return const Center(child: CircularProgressIndicator());
      },
    );
  }
}

main.dart :

import 'package:demo/home.dart';
import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

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

  static const String _title = 'Flutter Code Sample';

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: _title,
      home: HomeScreen(),
    );
  }
}

home.dart

import 'package:demo/future.dart';
import 'package:flutter/material.dart';

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

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: Center(child: Body()),
    );
  }
}


class Body extends FutureBuilderRequest {
  const Body({Key? key}) : super(key: key);

  @override
  Widget activeWidget() {
    return Container();
  }

  @override
  Future future() {
    return Future<String>.delayed(
      const Duration(seconds: 2),
          () => 'Data Loaded',
    );
  }

  @override
  Widget hasDataWidget(data) {
    return Column(
      children: [
        const Icon(
          Icons.check_circle_outline,
          color: Colors.green,
          size: 60,
        ),
        Padding(
          padding: const EdgeInsets.only(top: 16),
          child: Text('Result: ${data}'),
        )
      ],
    );
  }

  @override
  Widget noDataWidget() {
    return Container();
  }

  @override
  Widget noneConnectionWidget() {
    return Container();
  }

  @override
  Widget waitingWidget() {
    return const Center(
      child: CircularProgressIndicator(),
    );
  }
}
  • Related