Repository: flutter
Issue: [Proposal] FutureBuilder Rework
Labels: c: new feature,framework,c: proposal,P3,team-framework,triaged-framework

Hi there,

When using FutureBuilder, I feel like the user is almost "forced" to mix Logic and Widget Building, as the `snapshot` and `initialData` variables feel too broad / spaghetti code-prone for handling a `Future`.
When a Future completes with value, usually data is updated (and possibly some logic is implemented) and a new UI is returned, all simultaneously, but I think that this should be handled separately. The same should apply with other Future cases (Future uncomplete, Future completed with error).
**Note.** This issue could address what's stated in  #16218, as there's no clear explanation onto where (or what) to put the logic in the builder method.

## Use case
Example code:
```dart
class MyWidget extends StatefulWidget {
  @override
  _MyWidgetState createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  Future<String>? myFuture;
  MyClass myObjectWithDataToBeUpdated;

  @override
  void initState() {
    myFuture = Future.delayed(Duration(seconds: 5));
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<String>(
      initialData: "Wait for me!",
      future: this.myFuture,
      builder: (ctx, snapshot) {
        if (snapshot.hasError) {
          // Some logic, that imo shouldn't be here... that's bad.
          myObjectWithDataToBeUpdated.updateData(snapshot.error.toString());

          // Let's inform the user about this error.
          return Center(  // This is a Widget and that makes sense in this builder function!
            child: Text("Aww, snap! Try again later."),
          );
        }

        if (snapshot.connectionState != ConnectionState.done) {
          // We're not done, let's wait and update the UI.
          return CircularProgressIndicator();  // Again, widget building, good.
        }

        // Ok, if we're here, we're done.
        // We need to handle some logic first, and again, this shouldn't be here...
        myObjectWithDataToBeUpdated.updateData(snapshot.data);  // not good
        
        return Center(  // finally, and that's good again, a Widget.
          child: Text("Thank you for waiting, we're done."),
        );
      },
    );
  }
}
```

## Proposal
I'd like FutureBuilder to have separate future-handling options / properties, in which we can more explicitly distinguish between logic and UI building in response to the Future three states, "uncompleted", "completed with value", "completed with error".
Example code:
```dart
class MyWidget extends StatefulWidget {
  @override
  _MyWidgetState createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  Future<String>? myFuture;
  var myObjectWithDataToBeUpdated = MyClass();

  @override
  void initState() {
    myFuture = Future.delayed(Duration(seconds: 5));
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<String>(
      future: myFuture,
      onUncomplete: (initalData, connectionState) {
        // Some logic,eventually
        return;
      },
      uncompleteBuilder: (ctx, initialData) {
        Center(child: CircularProgressIndicator());
      },
      onComplete: (value) {
        myObjectWithDataToBeUpdated.updateData(value);
      },
      completeBuilder: (ctx, value) {
        Center(child: Text("We got $value."));
      },
      onError: (error) {
        myObjectWithDataToBeUpdated.updateData(error);
      },
      errorBuilder: (ctx, err){
          Center(child: Text("Something went wrong. $err"));
      },
    );
  }
}
```

I hope this is helpful / clear, and that I'm not wasting anyone's time.