This function was mainly taught to me by the teacher who learned the video of Flutter. I feel the effect is good, so I would like to share it and record it so that I can use it more conveniently next time. The code is mainly from the VIDEO of the MOOCs teacher. It is very well taught. If you want to learn about flutter, you can watch this video.

The main technologies used in Demo are as follows: back end: JSON Data front end: 1.TabBar+TabBarView to implement Tab switching 2. Use the flutter_staggered_grid_view plugin to implement the photo wall function 3. Use the Card component to make your interface more beautiful.

The code structure is as follows:

1. Convert JSON data to DART (Model layer)

Dart travel_model.dart page This page is mainly used to get the data displayed on the photo wall. I won’t go into detail here, because I’m mainly talking about the implementation of the photo wall, and there is a lot of code. If you use a tool, it is easy after all, just paste json data into the dart page. Json to Dart tool. If you want to see what json data is, look at this address. www.devio.org/io/flutter_…

/// page model
class TravelItemModel {
  int totalCount;
  List<TravelItem> resultList;

  TravelItemModel({this.totalCount, this.resultList});

  TravelItemModel.fromJson(Map<String, dynamic> json) {
    totalCount = json['totalCount'];
    if (json['resultList'] != null) {
      resultList = new List<TravelItem>();
      json['resultList'].forEach((v) {
        resultList.add(newTravelItem.fromJson(v)); }); }}Map<String, dynamic> toJson(a) {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['totalCount'] = this.totalCount;
    if (this.resultList ! = null) { data['resultList'] = this.resultList.map((v) => v.toJson()).toList();
    }
    returndata; }}class TravelItem {
  int type;
  Article article;

  TravelItem({this.type, this.article});

  TravelItem.fromJson(Map<String, dynamic> json) {
    type = json['type'];
    article =
    json['article'] != null ? new Article.fromJson(json['article']) : null;
  }

  Map<String, dynamic> toJson(a) {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['type'] = this.type;
    if (this.article ! = null) { data['article'] = this.article.toJson();
    }
    returndata; }}class Article {
  int articleId;
  String articleType;
  int productType;
  int sourceType;
  String articleTitle;
  Author author;
  List<Images> images;
  bool hasVideo;
  int readCount;
  int likeCount;
  int commentCount;
  List<Urls> urls;
  List<Null> tags;
  List<Topics> topics;
  List<Pois> pois;
  String publishTime;
  String publishTimeDisplay;
  String shootTime;
  String shootTimeDisplay;
  int level;
  String distanceText;
  bool isLike;
  int imageCounts;
  bool isCollected;
  int collectCount;
  int articleStatus;
  String poiName;

  Article(
      {this.articleId,
        this.articleType,
        this.productType,
        this.sourceType,
        this.articleTitle,
        this.author,
        this.images,
        this.hasVideo,
        this.readCount,
        this.likeCount,
        this.commentCount,
        this.urls,
        this.tags,
        this.topics,
        this.pois,
        this.publishTime,
        this.publishTimeDisplay,
        this.shootTime,
        this.shootTimeDisplay,
        this.level,
        this.distanceText,
        this.isLike,
        this.imageCounts,
        this.isCollected,
        this.collectCount,
        this.articleStatus,
        this.poiName});

  Article.fromJson(Map<String, dynamic> json) {
    articleId = json['articleId'];
    articleType = json['articleType'];
    productType = json['productType'];
    sourceType = json['sourceType'];
    articleTitle = json['articleTitle'];
    author =
    json['author'] != null ? new Author.fromJson(json['author']) : null;
    if (json['images'] != null) {
      images = new List<Images>();
      json['images'].forEach((v) {
        images.add(new Images.fromJson(v));
      });
    }
    hasVideo = json['hasVideo'];
    readCount = json['readCount'];
    likeCount = json['likeCount'];
    commentCount = json['commentCount'];
    if (json['urls'] != null) {
      urls = new List<Urls>();
      json['urls'].forEach((v) {
        urls.add(new Urls.fromJson(v));
      });
    }
    if (json['topics'] != null) {
      topics = new List<Topics>();
      json['topics'].forEach((v) {
        topics.add(new Topics.fromJson(v));
      });
    }
    if (json['pois'] != null) {
      pois = new List<Pois>();
      json['pois'].forEach((v) {
        pois.add(new Pois.fromJson(v));
      });
    }
    publishTime = json['publishTime'];
    publishTimeDisplay = json['publishTimeDisplay'];
    shootTime = json['shootTime'];
    shootTimeDisplay = json['shootTimeDisplay'];
    level = json['level'];
    distanceText = json['distanceText'];
    isLike = json['isLike'];
    imageCounts = json['imageCounts'];
    isCollected = json['isCollected'];
    collectCount = json['collectCount'];
    articleStatus = json['articleStatus'];
    poiName = json['poiName'];
  }

  Map<String, dynamic> toJson(a) {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['articleId'] = this.articleId;
    data['articleType'] = this.articleType;
    data['productType'] = this.productType;
    data['sourceType'] = this.sourceType;
    data['articleTitle'] = this.articleTitle;
    if (this.author ! = null ) { data['author'] = this.author.toJson();
    }
    if (this.images ! = null) { data['images'] = this.images.map((v) => v.toJson()).toList();
      print("Image"+data['images']);
    }
    data['hasVideo'] = this.hasVideo;
    data['readCount'] = this.readCount;
    data['likeCount'] = this.likeCount;
    data['commentCount'] = this.commentCount;
    if (this.urls ! = null) { data['urls'] = this.urls.map((v) => v.toJson()).toList();
    }
    if (this.topics ! = null) { data['topics'] = this.topics.map((v) => v.toJson()).toList();
    }
    if (this.pois ! = null) { data['pois'] = this.pois.map((v) => v.toJson()).toList();
    }
    data['publishTime'] = this.publishTime;
    data['publishTimeDisplay'] = this.publishTimeDisplay;
    data['shootTime'] = this.shootTime;
    data['shootTimeDisplay'] = this.shootTimeDisplay;
    data['level'] = this.level;
    data['distanceText'] = this.distanceText;
    data['isLike'] = this.isLike;
    data['imageCounts'] = this.imageCounts;
    data['isCollected'] = this.isCollected;
    data['collectCount'] = this.collectCount;
    data['articleStatus'] = this.articleStatus;
    data['poiName'] = this.poiName;
    returndata; }}class Author {
  int authorId;
  String nickName;
  String clientAuth;
  String jumpUrl;
  CoverImage coverImage;
  int identityType;
  String tag;

  Author(
      {this.authorId,
        this.nickName,
        this.clientAuth,
        this.jumpUrl,
        this.coverImage,
        this.identityType,
        this.tag});

  Author.fromJson(Map<String, dynamic> json) {
    authorId = json['authorId'];
    nickName = json['nickName'];
    clientAuth = json['clientAuth'];
    jumpUrl = json['jumpUrl'];
    coverImage = json['coverImage'] != null
        ? new CoverImage.fromJson(json['coverImage'])
        : null;
    identityType = json['identityType'];
    tag = json['tag'];
  }

  Map<String, dynamic> toJson(a) {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['authorId'] = this.authorId;
    data['nickName'] = this.nickName;
    data['clientAuth'] = this.clientAuth;
    data['jumpUrl'] = this.jumpUrl;
    if (this.coverImage ! = null) { data['coverImage'] = this.coverImage.toJson();
    }
    data['identityType'] = this.identityType;
    data['tag'] = this.tag;
    returndata; }}class CoverImage {
  String dynamicUrl;
  String originalUrl;

  CoverImage({this.dynamicUrl, this.originalUrl});

  CoverImage.fromJson(Map<String, dynamic> json) {
    dynamicUrl = json['dynamicUrl'];
    originalUrl = json['originalUrl'];
  }

  Map<String, dynamic> toJson(a) {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['dynamicUrl'] = this.dynamicUrl;
    data['originalUrl'] = this.originalUrl;
    returndata; }}class Images {
  int imageId;
  String dynamicUrl;
  String originalUrl;
  double width;
  double height;
  int mediaType;
  bool isWaterMarked;

  Images(
      {this.imageId,
        this.dynamicUrl,
        this.originalUrl,
        this.width,
        this.height,
        this.mediaType,
        this.isWaterMarked});

  Images.fromJson(Map<String, dynamic> json) {
    imageId = json['imageId'];
    dynamicUrl = json['dynamicUrl'];
    originalUrl = json['originalUrl'];
    width = json['width'];
    height = json['height'];
    mediaType = json['mediaType'];
    isWaterMarked = json['isWaterMarked'];
  }

  Map<String, dynamic> toJson(a) {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['imageId'] = this.imageId;
    data['dynamicUrl'] = this.dynamicUrl;
    data['originalUrl'] = this.originalUrl;
    data['width'] = this.width;
    data['height'] = this.height;
    data['mediaType'] = this.mediaType;
    data['isWaterMarked'] = this.isWaterMarked;
    returndata; }}class Urls {
  String version;
  String appUrl;
  String h5Url;
  String wxUrl;

  Urls({this.version, this.appUrl, this.h5Url, this.wxUrl});

  Urls.fromJson(Map<String, dynamic> json) {
    version = json['version'];
    appUrl = json['appUrl'];
    h5Url = json['h5Url'];
    wxUrl = json['wxUrl'];
  }

  Map<String, dynamic> toJson(a) {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['version'] = this.version;
    data['appUrl'] = this.appUrl;
    data['h5Url'] = this.h5Url;
    data['wxUrl'] = this.wxUrl;
    returndata; }}class Topics {
  int topicId;
  String topicName;
  int level;

  Topics({this.topicId, this.topicName, this.level});

  Topics.fromJson(Map<String, dynamic> json) {
    topicId = json['topicId'];
    topicName = json['topicName'];
    level = json['level'];
  }

  Map<String, dynamic> toJson(a) {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['topicId'] = this.topicId;
    data['topicName'] = this.topicName;
    data['level'] = this.level;
    returndata; }}class Pois {
  int poiType;
  int poiId;
  String poiName;
  int businessId;
  int districtId;
  PoiExt poiExt;
  int source;
  int isMain;
  bool isInChina;
  String countryName;

  Pois(
      {this.poiType,
        this.poiId,
        this.poiName,
        this.businessId,
        this.districtId,
        this.poiExt,
        this.source,
        this.isMain,
        this.isInChina,
        this.countryName});

  Pois.fromJson(Map<String, dynamic> json) {
    poiType = json['poiType'];
    poiId = json['poiId'];
    poiName = json['poiName'];
    businessId = json['businessId'];
    districtId = json['districtId'];
    poiExt =
    json['poiExt'] != null ? new PoiExt.fromJson(json['poiExt']) : null;
    source = json['source'];
    isMain = json['isMain'];
    isInChina = json['isInChina'];
    countryName = json['countryName'];
  }

  Map<String, dynamic> toJson(a) {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['poiType'] = this.poiType;
    data['poiId'] = this.poiId;
    data['poiName'] = this.poiName;
    data['businessId'] = this.businessId;
    data['districtId'] = this.districtId;
    if (this.poiExt ! = null) { data['poiExt'] = this.poiExt.toJson();
    }
    data['source'] = this.source;
    data['isMain'] = this.isMain;
    data['isInChina'] = this.isInChina;
    data['countryName'] = this.countryName;
    returndata; }}class PoiExt {
  String h5Url;
  String appUrl;

  PoiExt({this.h5Url, this.appUrl});

  PoiExt.fromJson(Map<String, dynamic> json) {
    h5Url = json['h5Url'];
    appUrl = json['appUrl'];
  }

  Map<String, dynamic> toJson(a) {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['h5Url'] = this.h5Url;
    data['appUrl'] = this.appUrl;
    returndata; }}Copy the code

The travel_tab_model.dart page is used to retrieve tabBar data for display, for example:

/// Travel photography category model
class TravelTabModel {
  Map params;
  String url;
  List<TravelTab> tabs;

  TravelTabModel({this.url, this.tabs});

  TravelTabModel.fromJson(Map<String, dynamic> json) {
    url = json['url'];
    params = json['params'];
    if (json['tabs'] != null) {
      tabs = new List<TravelTab>();
      json['tabs'].forEach((v) {
        tabs.add(newTravelTab.fromJson(v)); }); }}Map<String, dynamic> toJson(a) {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['url'] = this.url;
    if (this.tabs ! = null) { data['tabs'] = this.tabs.map((v) => v.toJson()).toList();
    }
    returndata; }}class TravelTab {
  String labelName;
  String groupChannelCode;

  TravelTab({this.labelName, this.groupChannelCode});

  TravelTab.fromJson(Map<String, dynamic> json) {
    labelName = json['labelName'];
    groupChannelCode = json['groupChannelCode'];
  }

  Map<String, dynamic> toJson(a) {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['labelName'] = this.labelName;
    data['groupChannelCode'] = this.groupChannelCode;
    returndata; }}Copy the code

2. Get JSON data via HTTP request (DAO layer)

The travel_dao.dart page first I had to import the HTTP request dependencies:

HTTP: ^ 0.12.0 + 1Copy the code

Utf8Decoder Utf8Decoder = Utf8Decoder(); Is mainly used to avoid Chinese garbled problem, among which the following parameters in Params mainly use anti-crawl function, because the data is dynamic change.

import 'dart:async';
import 'dart:convert';

import 'package:http/http.dart' as http;
import 'package:trip_demo/model/travel_model.dart';

/// page interface

var Params = {
  "districtId": - 1."groupChannelCode": "RX-OMF"."type": null,
  "lat": - 180.."lon": - 180.."locatedDistrictId": 0."pagePara": {
    "pageIndex": 1."pageSize": 10."sortType": 9."sortDirection": 0
  },
  "imageCutType": 1."head": {'cid': "09031014111431397988"},
  "contentType": "json"
};

class TravelDao {
  static Future<TravelItemModel> fetch(
      String url,Map params, String groupChannelCode, int pageIndex, int pageSize) async {
    Map paramsMap = params['pagePara'];
    paramsMap['pageIndex'] = pageIndex;
    paramsMap['pageSize'] = pageSize;
    params['groupChannelCode'] = groupChannelCode;
    final response = await http.post(url, body: jsonEncode(params));
    if (response.statusCode == 200) {
      Utf8Decoder utf8decoder = Utf8Decoder(); // fix Chinese garbled characters
      var result = json.decode(utf8decoder.convert(response.bodyBytes));
      return TravelItemModel.fromJson(result);
    } else {
      throw Exception('Failed to load travel'); }}}Copy the code

Travel_tab_dao page

import 'dart:async';
import 'dart:convert';

import 'package:http/http.dart' as http;
import 'package:trip_demo/model/travel_tab_model.dart';

/// The interface of the camera category
class TravelTabDao {
  static Future<TravelTabModel> fetch(a) async {
    final response = await http
        .get('http://www.devio.org/io/flutter_app/json/travel_page.json');
    if (response.statusCode == 200) {
      Utf8Decoder utf8decoder = Utf8Decoder(); // fix Chinese garbled characters
      var result = json.decode(utf8decoder.convert(response.bodyBytes));
      return TravelTabModel.fromJson(result);
    } else {
      throw Exception('Failed to load travel_page.json'); }}}Copy the code

3. Basic functions of demo interface (View layer)

First take a look at the structure of the Main. dart interface:

component function
TabBar Realize the TAB theme switch
UnderlineIndicator The main implementation is a progress bar that appears below the theme when tabbing. It makes scrolling easier because the plugin is not included with the flutter and must be importedUnderline_indicator: ^ hundreds
Flexible Generally used within row and column layouts, it functions like ExPanded, except thatFlexible does not automatically fill blank areas
TabBarView The page used to hold the switch
TravelTabPage This is our custom component, which we will describe in detail below
import 'package:flutter/material.dart';
import 'package:underline_indicator/underline_indicator.dart';

import 'dao/travel_tab_dao.dart';
import 'model/travel_tab_model.dart';
import 'travel_tab_page.dart';

void main(a){
  runApp(MaterialApp(
        home: TravelPage(),
  ));
}

class TravelPage extends StatefulWidget {
  @override
  _TravelPageState createState(a) => _TravelPageState();
}

class _TravelPageState extends State<TravelPage>
    with TickerProviderStateMixin {
  TabController _controller;
  List<TravelTab> tabs = [];
  TravelTabModel travelTabModel;

  @override
  void initState(a) {
    _controller = TabController(length: 0, vsync: this);
    TravelTabDao.fetch().then((TravelTabModel model) {
      _controller = TabController(
          length: model.tabs.length, vsync: this); //fix TAB label blank problem
      setState(() {
        tabs = model.tabs;
        travelTabModel = model;
      });
    }).catchError((e) {
      print(e);
    });
    super.initState();
  }

  @override
  void dispose(a) {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Column(
          children: <Widget>[
            Container(
              color: Colors.white,
              padding: EdgeInsets.only(top: 30),
              child: TabBar(
                  controller: _controller,
                  isScrollable: true,
                  labelColor: Colors.black,
                  labelPadding: EdgeInsets.fromLTRB(20.0.10.5),
                  indicator: UnderlineIndicator(
                      strokeCap: StrokeCap.round,
                      borderSide: BorderSide(
                        color: Color(0xff2fcfbb),
                        width: 3,
                      ),
                      insets: EdgeInsets.only(bottom: 10)),
                  tabs: tabs.map<Tab>((TravelTab tab) {
                    return Tab(
                      text: tab.labelName,
                    );
                  }).toList()),
            ),
            Flexible(
                child: TabBarView(
                    controller: _controller,
                    children: tabs.map((TravelTab tab) {
                      returnTravelTabPage( travelUrl: travelTabModel.url, params: travelTabModel.params, groupChannelCode: tab.groupChannelCode, ); }).toList())) ], )); }}Copy the code

Travel_tab_page page

component function
LoadingContainer This custom method is mainly used to set the effect of a loop load before the data is loaded
RefreshIndicator Drop down the reloaded component
MediaQuery.removePadding Remove white space directly from components to make the UI look better
StaggeredGridView.countBuilder Used to implement the photo wall function, need to importFlutter_staggered_grid_view: ^ 0.3.4Plug-ins can do this
_TravelItem Custom components are covered below

After all, these components are basic, I will not go into detail, if you want to understand the basic components, you can go to see my following two articles:Learn about Flutter Day 4: A summary of the commonly used components of StatelessWidgets that hold up half the sky of Flutter.

Learn about Flutter Day 5: A summary of common components of StatefulWidget that hold up the other half of Flutter?

import 'package:flutter/material.dart';
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
import 'package:transparent_image/transparent_image.dart';

import 'dao/travel_dao.dart';
import 'method/loading_contain.dart';
import 'model/travel_model.dart';

const _TRAVEL_URL =
    'https://m.ctrip.com/restapi/soa2/16189/json/searchTripShootListForHomePageV2?_fxpcqlniredt=09031014111431397988&__gw_ap Pid = 99999999 & __gw_ver = 1.0 & __gw_from = 10650013707 & __gw_platform = H5 ';

const PAGE_SIZE = 10;

class TravelTabPage extends StatefulWidget {
  final String travelUrl;
  final Map params;
  final String groupChannelCode;

  const TravelTabPage(
      {Key key, this.travelUrl, this.params, this.groupChannelCode})
      : super(key: key);

  @override
  _TravelTabPageState createState(a) => _TravelTabPageState();
}

class _TravelTabPageState extends State<TravelTabPage>
    with AutomaticKeepAliveClientMixin {
  List<TravelItem> travelItems;
  int pageIndex = 1;
  bool _loading = true;
  ScrollController _scrollController = ScrollController();

  @override
  void initState(a) {
    _loadData();
    _scrollController.addListener(() {
      if (_scrollController.position.pixels ==
          _scrollController.position.maxScrollExtent) {
        _loadData(loadMore: true); }}); super.initState(); } @override
  void dispose(a) {
    _scrollController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    super.build(context);
    return Scaffold(
      body: LoadingContainer(
        isLoading: _loading,
        child: RefreshIndicator(
          onRefresh: _handleRefresh,
          child: MediaQuery.removePadding(
              removeTop: true,
              context: context,
              child: StaggeredGridView.countBuilder(
                controller: _scrollController,
                crossAxisCount: 4, itemCount: travelItems? .length ??0,
                itemBuilder: (BuildContext context, int index) => _TravelItem(
                  index: index,
                  item: travelItems[index],
                ),
                staggeredTileBuilder: (int index) => new StaggeredTile.fit(2)))))); }void _loadData({loadMore = false{})if (loadMore) {
      pageIndex++;
    } else {
      pageIndex = 1;
    }

    TravelDao.fetch(widget.travelUrl ?? _TRAVEL_URL, widget.params,
        widget.groupChannelCode, pageIndex, PAGE_SIZE)
        .then((TravelItemModel model) {
      _loading = false;
      setState(() {
        List<TravelItem> items = _filterItems(model.resultList);
        if(travelItems ! = null) { travelItems.addAll(items); }else{ travelItems = items; }}); }).catchError((e) { _loading =false;
      print(e);
    });
  }

  List<TravelItem> _filterItems(List<TravelItem> resultList) {
    if (resultList == null) {
      return [];
    }
    List<TravelItem> filterItems = [];
    resultList.forEach((item) {
      if(item.article ! = null) {// Remove the model where article is emptyfilterItems.add(item); }});return filterItems;
  }

  @override
  bool get wantKeepAlive => true;

  Future<Null> _handleRefresh() async {
    _loadData();
    returnnull; }}class _TravelItem extends StatelessWidget {
  final TravelItem item;
  final int index;

  const _TravelItem({Key key, this.item, this.index}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      child: Card(
        child: PhysicalModel(
          color: Colors.transparent,
          clipBehavior: Clip.antiAlias,
          borderRadius: BorderRadius.circular(5),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: <Widget>[
              _itemImage(context),
              Container(
                padding: EdgeInsets.all(4),
                child: Text(
                  item.article.articleTitle,
                  maxLines: 2,
                  overflow: TextOverflow.ellipsis,
                  style: TextStyle(fontSize: 14, color: Colors.black87),
                ),
              ),
              _infoText()
            ],
          ),
        ),
      ),
    );
  }

  _itemImage(BuildContext context) {
    final size = MediaQuery.of(context).size;
    return Stack(
      children: <Widget>[
        Container(
          // Set the minimum initial height to prevent jitter when moving image height
          constraints: BoxConstraints(
            minHeight: size.width / 2 - 10,
          ),
          child: FadeInImage.memoryNetwork(
            placeholder: kTransparentImage,
            image: item.article.images[0]? .dynamicUrl??"https://youimg1.c-ctrip.com/target/1A0n12000000sei6395D2_R_800_10000_Q50.jpg",
            fit: BoxFit.cover,
          ),
         // child: Image.network(item.article.images[0]?.dynamicUrl??"https://youimg1.c-ctrip.com/target/1A0n12000000sei6395D2_R_800_10000_ Q50.jpg"),
        ),
        Positioned(
            bottom: 8,
            left: 8,
            child: Container(
              padding: EdgeInsets.fromLTRB(5.1.5.1),
              decoration: BoxDecoration(
                  color: Colors.black54,
                  borderRadius: BorderRadius.circular(10)),
              child: Row(
                children: <Widget>[
                  Padding(
                      padding: EdgeInsets.only(right: 3),
                      child: Icon(
                        Icons.location_on,
                        color: Colors.white,
                        size: 12,
                      )),
                  LimitedBox(
                    maxWidth: 130,
                    child: Text(
                      _poiName(),
                      maxLines: 1,
                      overflow: TextOverflow.ellipsis,
                      style: TextStyle(color: Colors.white, fontSize: 12() [() [() [() [() [() } String _poiName() {return item.article.pois == null || item.article.pois.length == 0
        ? 'unknown'
        : item.article.pois[0]? .poiName ??'unknown';
  }

  _infoText() {
    return Container(
      padding: EdgeInsets.fromLTRB(6.0.6.10),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: <Widget>[
          Row(
            children: <Widget>[
              PhysicalModel(
                color: Colors.transparent,
                clipBehavior: Clip.antiAlias,
                borderRadius: BorderRadius.circular(12),
                child: Image.network(
                  item.article.author?.coverImage?.dynamicUrl,
                  width: 24,
                  height: 24,
                ),
              ),
              Container(
                padding: EdgeInsets.all(5),
                width: 90, child: Text( item.article.author? .nickName, maxLines:1,
                  overflow: TextOverflow.ellipsis,
                  style: TextStyle(fontSize: 12),
                ),
              )
            ],
          ),
          Row(
            children: <Widget>[
              Icon(
                Icons.thumb_up,
                size: 14,
                color: Colors.grey,
              ),
              Padding(
                padding: EdgeInsets.only(left: 3),
                child: Text(
                  item.article.likeCount.toString(),
                  style: TextStyle(fontSize: 10() [() [() [() [() [() }}Copy the code

Loading_contain. Dart page

import 'package:flutter/material.dart';

class LoadingContainer extends StatelessWidget {
  final Widget child;
  final bool isLoading;
  final bool cover;

  const LoadingContainer(
      {Key key,
        @required this.isLoading,
        this.cover = false,
        @required this.child})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return! cover ? ! isLoading ? child : _loadingView : Stack( children: <Widget>[child, isLoading ? _loadingView : Container()], ); } Widget get _loadingView {returnCenter( child: CircularProgressIndicator(), ); }}Copy the code