Since Flutter is built around the principle of Widget composition, it's common to have data spread across many nested widgets. For example, let's say we want to have a
PokemonList Widget that displays a list of
Our Widget tree might look like this:
Our first impulse might be to write a Query like this one:
And use it in the following Widgets:
While this works, it tightly couples our
PokemonCard Widgets which causes several disadvantages:
PokemonCardWidget can't be reused with data from other GraphQL Operations since it has an explicit dependency on the
AllPokemonQuery must keep track of the data requirements not only for our
PokemonListitself (in which the query is executed), but also for all child Widgets (i.e.
Colocation of Widgets and Data Requirements
A common pattern to overcome these issues is to colocate Widgets and their data requirements. In other words, each Widget should have a corresponding GraphQL definition that specifies only the data needed for that Widget.
A naive implementation of this (don't do this) might be to:
- Request only the
idfield in our
- Pass the
- Execute a
GetPokemonQuery in our
PokemonCardthat fetches the data only for that Pokemon
However, this would result in a seperate network request (and subsequent database query) for each Pokemon in the list. Not very efficient.
Istead, we can extract our
PokemonCard's data requirements into a Fragment:
PokemonCard can depend on the
This means the
PokemonCard Widget can be reused anywhere the
PokemonCardFragment is used. It also means that if our data requirements for
PokemonCard change (say, if we need to add a
height property), we only need to update our
AllPokemon Query and any other operations that use
PokemonCardFragment don't need to be updated.
This pattern leads to code that is easier to maintain, test, and reason about.
Fragments on Root Query
The above pattern works even if your data requriements for a single screen include multiple GraphQL queries since you can include Fragments on any GraphQL type, including the root
For example, let's say you want to add a user avatar Widget to the header of your
PokemonListScreen that shows the currently logged-in user.
You might structure your queries like so:
Even though you are fetching data from two different root queries (
user), you can use a single
Operation Widget which will make a single network request for the