This is the 16th day of my participation in the August More Text Challenge. For details, see: August More Text Challenge

preface

It’s been a while since I’ve talked about interfaces, so I’ll take a look at the header area of my homepage to talk about the Stack component of the Flutter and the FutureProvider of Provider. To make a statement, the imitation of the gold mobile terminal personal homepage head area as shown in the following figure, there are actually sliding at the bottom of the hover part is not complete, later have time to improve the whole interface, to a “fake” gold personal homepage.

Interface analysis

When we get the interface, we first analyze the structure of the interface. The head part from bottom to top consists of the following contents:

  • Base image and profile picture
    • Bottom Banner: at the bottom;
    • Profile picture: Include the background circle (or border) under the profile picture, superimposed on the bottom Banner;
  • Personal information such as user name, level, job information (title and company) and personal introduction: below the base picture and profile picture.
  • Follow, follow, and drive statistics: below user info.
  • Back button: The back button is at the top of the entire page so that it can be clicked back at any time.

Get the layout structure of our page as shown in the figure below. Where heads andBannerBecause it overlaps, I need to use oneStackComponent packages, the blue area.

Component structure

After the layout is determined, we can determine which components to use. The corresponding components for each layout area are as follows:

  • Back button:IconButton, using a back toon button, click to return to the previous page.
  • Banner:CachedImageNetWorkSo that the network images can be loaded.
  • Avatars: Use oneStackAt the bottom is the circle that implements the borderContainer, the upper is the circular head, usingContainer+CachedImageNetworkThe implementation. And the entire profile picture areaPositionedThe absolute position layout, keeping the left and back buttons aligned, is then superimposed by a half areaBannerOn.
  • Personal information area: Use column layoutColumnThe information is arranged, and then nicknames and levels are laid out in rowsRow.
  • Statistics area: Use row layoutRowArrange the individual statistics and use the statistics themselvesColumnLayout numbers and data item names.

The entire page uses the CustomScrollView and then a Stack wraps around the entire page and the back button with the absolute positioning of the back button Positioned in the top left corner. The final component tree is as follows (the Container component is omitted).

Focus on the Stack and Postioned components. The Stack consists of Stack and IndexedStack. The IndexedStack, which we described in Introduction to Flutter and Practical iii: Building a common page frame, is actually an ordered Stack that displays the level of the interface by controlling the current number. The children of a Stack are a set of components, using a Stack sort. The higher the order, the higher the hierarchy (last in, first out). In this way, multiple interfaces can be displayed in layers.

C-modules are specialized submodules of the Stack that control the position of the submodules of the Stack in relation to the Stack, which can be controlled by the left, top, right, bottom and height properties.

const Positioned({
  Key? key,
  this.left,
  this.top,
  this.right,
  this.bottom,
  this.width,
  this.height,
  required Widget child,
}) : assert(left == null || right == null || width == null),
     assert(top == null || bottom == null || height == null),
     super(key: key, child: child);
Copy the code

The specific rules are as follows:

  • iftopProperty is not empty, positioning the top of the component toStackAssembly top distancetopThe position of the unit. Other such asleft.rightbottomThe mechanism is similar.
  • iftopbottomAre not empty, then the component is constrained to the height of its layout in the Stack (fixed distance from the upper and lower boundaries).leftrightWhen it is not empty, it limits the width constraint.
  • iftopandbottomOnly one is not empty, so you can specify the height. ifleftrightOnly one is not empty, so you can specify the width.
  • left,rightwidthAt least one of the three properties is not null;top.bottomheightAlso the same.

For example, if we want to locate the profile image component, we can set the left distance to 20, and the vertical distance to be the height of the Banner image minus half of the height of the profile image component itself (equal to the radius of the profile image plus the border size) so that the profile image overlaps the Banner image.

Positioned(
    left: 20,
    top: imageHeight - avatarRadius - avatarBorderSize,
    child: _getAvatar(
      personalProfile.avatar,
      avatarRadius * 2,
      avatarBorderSize,
    ),
  ),
Copy the code

You can download the code here (interface file: personal_homepage.dart) : Status management code.

Stack(
  children: [
    CustomScrollView(
      slivers: [
        _getBannerWithAvatar(context, personalProfile),
        _getPersonalProfile(personalProfile),
        _getPersonalStatistic(personalProfile),
      ],
    ),
    Positioned(
      top: 40,
      left: 10,
      child: IconButton(
        onPressed: () {
          Navigator.of(context).pop();
        },
        icon: Icon(
          Icons.arrow_back,
          color: Colors.white,
        ),
      ),
    ),
  ],
);
Copy the code

Interface Data Acquisition

From the browser developer tools personal homepage interface for grabbing the nuggets: https://api.juejin.cn/user_api/v1/user/get?user_id= {user_id}, interface returned many data items, extract the data format we need are as follows:

{
    "err_no": 0."err_msg": "success"."data": {
        "user_id": "70787819648695"."user_name": "Island coders"."company": "Island coders"."job_title": "Public Account"."avatar_large": "https://sf3-ttcdn-tos.pstatp.com/img/user-avatar/097899bbe5d10bceb750d5c69415518a~300x300.image"."level": 3."power": 2193."description": "An amateur coder who floats from south to north, north to south."."github_verified": 1."followee_count": 148."follower_count": 440,}}Copy the code

Then you build the entity class based on this data, the personal_entity.dart file in the source code. The corresponding interface request service is as follows:

class JuejinService { static Future<PersonalEntity? > getPersonalProfile(String userId) async { var response = await HttpUtil.getDioInstance() .get('https://api.juejin.cn/user_api/v1/user/get?user_id=$userId');
    if (response.statusCode == 200) {
      if (response.data['err_no'] == 0) {
        return PersonalEntity.fromJson(response.data['data']);
      }
    }

    return null; }}Copy the code

Future State Management

The state management of The Provider provides a faster and easier way to operate Future objects asynchronously. That is the FutureProvider. Remember our dynamic detail page, because we need to request the data before we can refresh the interface, we changed the detail page to the StatefulWidget to request the data in initState.

@override void initState() { super.initState(); context.read<DynamicModel>().getDynamic(widget.id).then((success) { if (success) { context.read<DynamicModel>().updateViewCount(widget.id); }}); }Copy the code

With the FutureProvider, you can place the request on the FutureProvider. The FutureProvider initiates the asynchronous operation and automatically notifies the lower-level components when the Future asynchronous operation is complete, making it possible to complete the network request without using the StatefulWidget. FutureProvider is used in a similar way to ChangeNotiferProvider, as shown below, where initialValue is the initial data and can be null:

/ / createFutureProvider<T? >( initialValue:null,
  create: (context) => Future,
  child: MyApp(),
)

/ / valueFutureProvider<T? >.value( value: Future, initialData:null,
	child: MyApp(),
}
Copy the code

From here, we can use FutureProvider to automatically refresh the interface after completing the personal information request. The related code is as follows:

class PersonalHomePageWrapper extends StatelessWidget { const PersonalHomePageWrapper({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return FutureProvider<PersonalEntity? >( create: (context) => JuejinService.getPersonalProfile('70787819648695'),
      initialData: null, child: _PersonalHomePage(), ); } } class _PersonalHomePage extends StatelessWidget { const _PersonalHomePage({Key? key}) : super(key: key); @override Widget build(BuildContext context) { PersonalEntity? personalProfile = context.watch<PersonalEntity? > (); If (personalProfile == null) {return Center(child: Text(' loading... ')); } return Stack(/ /... Omit interface code
  	);
	}
	/ /... Omit interface code
}
Copy the code

The results

It looks like this. Does it look like the Nuggets’ homepage?

conclusion

This paper mimises the top section of the diggard homepage interface, through which we analyze the layout and the hierarchy of modules, focusing on the use of Stack and Stack modules, as well as the use of FutureProvider to automatically notify the interface to refresh after asynchronous operations, thus simplifying our page code. For example, you don’t need to use initState and write a state management class. Everyone implements the interface differently, but the idea is the same:

  • Analyze the layout of UI design draft;
  • Divide the code hierarchy into large layout blocks.
  • Refine the layout blocks to build the UI component tree.
  • Extract possible common parts of the interface to improve reusability, such as the statistics in this article, which has certain universality and can be extracted separately from components.

I am an island code farmer, wechat public account with the same name, this is the entry and combat column Flutter, the corresponding source code please see here: Entry and combat column source code.

👍🏻 : feel a harvest please point to encourage!

🌟 : Collect articles, convenient to read back!

💬 : Comment exchange, mutual progress!