skip to Main Content

I always do the same thing with a FutureBuilder in my flutter app, so I want to get rid of the duplicated code and provide a widget for that.
I have an abstract interface class Service and my widget uses the generic types <T extends Object, S extends Service>. The class AuthenticationService implements my Service interface.

My widget looks like this:

class NewStandardFutureBuilder<T extends Object, S extends Service>
    extends StatefulWidget {
  const NewStandardFutureBuilder({
    super.key,
    required this.service,
    required this.createFuture,
    required this.builder,
  });

  final S service;
  final Future<T> Function(S) createFuture;
  final DataWidgetBuilder<T> builder;

  @override
  State<NewStandardFutureBuilder> createState() =>
      _NewStandardFutureBuilderState<T, S>();
}

class _NewStandardFutureBuilderState<T extends Object, S extends Service>
    extends State<NewStandardFutureBuilder> {
  late Future<T> future;

  @override
  void initState() {
    super.initState();
    future = widget.createFuture(widget.service) as Future<T>;
  }

  Future<void> load() async {
    setState(() {
      future = widget.createFuture(widget.service as S) as Future<T>;
    });
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<T>(
      future: future,
      builder: (context, snapshot) {...},
    );
  }
}

I try to use the widget like this:

return NewStandardFutureBuilder<String, AuthenticationService>(
  service: AuthenticationService(),
  createFuture: (service) => service.getUsername(),
  builder: (username) => Text('You are logged in as $username'),
);

The class AuthenticationService implements the Service interface. I get the error in the initState-method:

type '(AuthenticationService) => Future<String>' is not a subtype of type '(Service) => Future<Object>'

How do I fix this problem?
Thanks!

2

Answers


  1. From, the documentation on substituting types:

    When substituting types, it helps to think in terms of consumers and producers. A consumer absorbs a type and a producer generates a type.

    You can replace a consumer’s type with a supertype and a producer’s type with a subtype.

    Here, the parameter of the function is a consumer, so you can substitute it with a supertype, but not subtype.

    You can see the example on the methods section. Passing callback function is a similar case to overriding method, as shown in the example.


    For the solution, you might want to write your code in algebraic data types. It shows an example of having a single method that covers two different types under the same supertype like this:

    sealed class Shape {}
    
    class Square implements Shape {
      final double length;
      Square(this.length);
    }
    
    class Circle implements Shape {
      final double radius;
      Circle(this.radius);
    }
    
    double calculateArea(Shape shape) => switch (shape) {
          Square(length: var l) => l * l,
          Circle(radius: var r) => math.pi * r * r
        };
    
    Login or Signup to reply.
  2. You forgot to make sure your state gets a properly typed widget:

    It needs to include <T, S>:

     extends State<NewStandardFutureBuilder<T,S>> {
    

    Then you will get about 3 errors of "unneccessary cast", so just remove those, they are indeed unneccessary.

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