使用Flutter App Template Generater快速开发应用

如何快速、优雅的创建应用,减少敲代码的时间,减少后期维护的时间,一直都是码农们追求的目标之一。我们做我们所想的,创造性的工作,但是我们不想重复敲同样的代码。
前段时间介绍了Flutter App Template Generater的基本功能,现在用它来创建一个像样的例子。基于 Unsplash Api来浏览图片的例子。先来几张图:

使用Flutter App Template Generater快速开发应用_第1张图片
discover dark

使用Flutter App Template Generater快速开发应用_第2张图片
collection list dark

使用Flutter App Template Generater快速开发应用_第3张图片
settings dark

初始化工程

  1. 安装插件到Android Studio。
  2. 用Android Studio生成一个项目photo。
  3. 右击项目目录的lib,选择New --> Generate App Template。
  4. 点击“init project”按钮,这样就初始化完成。

创建主页面HomeView

  1. 同样右击lib打开插件。
  2. 在PageName输入Home,选择BottomTabBar来生成底部导航栏。在Model Entry Name输入数据类名User,从 Unsplash User Api复制一个User对象的json数据到json编辑框,点击OK进行下一步。
  3. 在类的编辑对话框中,把User类的id设为unique,必须有一个唯一的属性,因为是根据这个唯一的属性来做查找的。为时间类型的字段选择Datetime类型,点击生成。

创建第一个页面DiscoverView

  1. 同样右击lib打开插件。
  2. 在Page Name输入"Discover" ,选择: Query、AppBar、TopTabBar、ActionButton、Search。在Model Entry Name输入Photo,从 Unsplash Photo Api复制Photo的json对象到编辑区。点击OK进行下一步。
  3. 设置id为unique,生成,前面生成的类,再次生成会询问要不要覆盖的。.

创建第二个页面CollectListView

打开插件,输入页面名称CollectList,选择基本界面元素Query、AppBar、ListView、ActionButton、Search。输入数据类名Collection,从Unsplash Collection Api复制Collection数据对象。点击OK进行下一步生成页面。

创建第三个页面MeView

打开插件,输入Me和选择想要的界面元素。选择UI only,因为前面已经生成了User类,无需再生成。但是还是要再Model Entry Name中输入User,生成页面。

修改页面导航

main.dart

Map _routes() {
   return {
     "/settings": (_) => SettingsOptionsPage(
           options: _options,
           onOptionsChanged: _handleOptionsChanged,
         ),
     "/": (_) => new HomeView(),
   };
 }

home_view.dart

   widget = PageView(
       children: [DiscoverView(), CollectListView(), MeView()],

配置服务器信息

根据Unsplash Public Action Api 修改服务器地址和Clent-ID,network_common.dart

    // address
    dio.options.baseUrl = 'https://api.unsplash.com/';
    
    // authentication info
    options.headers["Authorization"] =
          "Client-ID xxxxxxxxx";

创建最新Photo页面

打开插件,输入页面名称Photo,选择UI only,Query,ListView。输入medel名称Photo,生成页面。

编辑photo网络接口

根据Unsplash list-photo Api 的指示修改photo_repository.dart

   Future getPhotosList(String sorting, int page, int limit) {
    return new NetworkCommon().dio.get("photos", queryParameters: {
      "order_by": sorting,
      "page": page,
      "per_page": limit
    }).then((d) {
      var results = new NetworkCommon().decodeResp(d);
      Page page = new NetworkCommon().decodePage(d);
      page.data =
      results.map((item) => new Photo.fromJson(item)).toList();
      return page;
    });
  }

修改中间件

编辑photo_middleware.dart

Middleware _createGetPhotos(PhotoRepository repository) {
  return (Store store, dynamic action, NextDispatcher next) {
    if (checkActionRunning(store, action)) return;
    running(next, action);
    int num = store.state.photoState.page.next;
    if (action.isRefresh) {
      num = 1;
    } else {
      if (store.state.photoState.page.next <= 0) {
        noMoreItem(next, action);
        return;
      }
    }
    repository.getPhotosList(action.orderBy, num, 10).then((page) {
      next(SyncPhotosAction(page: page, photos: page.data));
      completed(next, action);
    }).catchError((error) {
      catchError(next, action, error);
    });
  };
}

编辑photo_actions.dart

// add orderBy property
class GetPhotosAction {
  final String actionName = "GetPhotosAction";
  final bool isRefresh;
  final String orderBy;

  GetPhotosAction({this.orderBy, this.isRefresh});
}

编辑 photo_view.dart

我们使用下面两个插件来显示photo列表,在pubspec.yaml引入它们。

  cached_network_image: ^0.7.0
  flutter_staggered_grid_view: ^0.2.7

修改 photo_view.dart

// define orderBy property in PhotoView
class PhotoView extends StatelessWidget {
  final String orderBy;

//build()
    widget = NotificationListener(
        onNotification: _onNotification,
        child: RefreshIndicator(
            key: _refreshIndicatorKey,
            onRefresh: _handleRefresh,
            child: new StaggeredGridView.countBuilder(
              controller: _scrollController,
              crossAxisCount: 2,
              itemCount: this.widget.viewModel.photos.length,
              itemBuilder: (_, int index) => _createItem(context, index),
              staggeredTileBuilder: (int index) => new StaggeredTile.fit(1),
              mainAxisSpacing: 0.0,
              crossAxisSpacing: 0.0,
            )));
// _createItem()
  _createItem(BuildContext context, int index) {
    if (index < this.widget.viewModel.photos?.length) {
      return Container(
          padding: EdgeInsets.all(2.0),
          child: Stack(
            children: [
              Hero(
                tag: this.widget.viewModel.photos[index].id,
                child: InkWell(
                  onTap: () => Navigator.push(
                        context,
                        MaterialPageRoute(
                          builder: (context) =>
                              ViewPhotoView(id: 0, pageIndex: index),
                        ),
                      ),
                  child: new CachedNetworkImage(
                    imageUrl: this.widget.viewModel.photos[index].urls.small,
                    placeholder: (context, url) =>
                        new CircularProgressIndicator(),
                    errorWidget: (context, url, error) => new Icon(Icons.error),
                  ),
                ),
              ),
            ],
          ),
          decoration: BoxDecoration(
              border: Border(
                  bottom: BorderSide(color: Theme.of(context).dividerColor))));
    }

添加属性orderBy到getPhotos中。
photo_view_model.dart

  final Function(bool, String) getPhotos;
 
      getPhotos: (isRefresh, orderBy) {
        store.dispatch(GetPhotosAction(isRefresh: isRefresh,orderBy: orderBy));
      },

添加PhotoView 到 DiscoverView

  TabController _controller;
  List _tabs = [
    "Latest",
 //will add some photos of some collection here
  ];
  List _views = [
    0,
  ];
 
 // init TabController
  TabController _makeNewTabController() => TabController(
        vsync: this,
        length: _tabs.length,
      );

  Widget build(BuildContext context) {
    var widget;

    widget = TabBarView(
      key: Key(Random().nextDouble().toString()),
      controller: _controller,
      children: _views.map((id) {
        if (id == 0) {
          return PhotoView(orderBy: "latest");
        } else {
          return CollectionView(collection: id);
        }
      }).toList(),
    );
    return Scaffold(
      appBar: AppBar(
        bottom: TabBar(
          controller: _controller,
          isScrollable: true,
          tabs: _tabs.map((title) => Tab(text: title)).toList(),
        ),
        title: Text("Discover"),
        actions: _buildActionButton(),
      ),
      body: widget,
    );
  }

now you can the app.

下一步创建 CollectionView 及其 中间件

添加新的Action到中间件

在 photo_state.dart 添加一个变量来存每个集合的照片。

  final Map collectionPhotos;

在 photo_actions.dart添加新Action类

class SyncCollectionPhotosAction {
  final String actionName = "SyncCollectionPhotosAction";
  final Page page;
  final int collectionId;

  SyncCollectionPhotosAction({this.collectionId, this.page});
}

编辑 photo_reducer.dart,更新middleware传来的数据到state,以此Redux来通知UI状态改变,从而更新UI。

  TypedReducer(_syncCollectionPhotos),
  
PhotoState _syncCollectionPhotos(
    PhotoState state, SyncCollectionPhotosAction action) {
  state.collectionPhotos.update(action.collectionId, (v) {
    v.id = action.collectionId;
    v.page?.last = action.page?.last;
    v.page?.prev = action.page?.prev;
    v.page?.first = action.page?.first;
    v.page?.next = action.page?.next;
    for (var photo in action.page?.data) {
      v.photos
          ?.update(photo.id.toString(), (vl) => photo, ifAbsent: () => photo);
    }
    return v;
  }, ifAbsent: () {
    PhotoOfCollection pc = new PhotoOfCollection();
    pc.id = action.collectionId;
    Page page = Page();
    page.last = action.page?.last;
    page.prev = action.page?.prev;
    page.first = action.page?.first;
    page.next = action.page?.next;
    pc.page = page;
    pc.photos = Map();
    for (var photo in action.page?.data) {
      pc.photos
          ?.update(photo.id.toString(), (v) => photo, ifAbsent: () => photo);
    }
    return pc;
  });
  return state.copyWith(collectionPhotos: state.collectionPhotos);
}

编辑photo_middleware.dart,捕捉UI的action,并从网络获取数据或者从本地数据库。

//add new action to list
  final getCollectionPhotos = _createGetCollectionPhotos(_repository);
  
    TypedMiddleware(getCollectionPhotos),

// new handle function
Middleware _createGetCollectionPhotos(PhotoRepository repository) {
  return (Store store, dynamic action, NextDispatcher next) {
    if (checkActionRunning(store, action)) return;
    running(next, action);
    int num =
        store.state.photoState.collectionPhotos[action.id]?.page?.next ?? 1;
    if (action.isRefresh) {
      num = 1;
    } else {
      if (store.state.photoState.collectionPhotos[action.id] != null &&
          store.state.photoState.collectionPhotos[action.id].page.next <= 0) {
        noMoreItem(next, action);
        return;
      }
    }
    repository.getCollectionPhotos(action.id, num, 10).then((page) {
      next(SyncCollectionPhotosAction(collectionId: action.id, page: page));
      completed(next, action);
    }).catchError((error) {
      catchError(next, action, error);
    });
  };
}

添加新的Photo api,Unsplash collection api

  Future getCollectionPhotos(int id, int page, int limit) {
    return new NetworkCommon().dio.get("collections/${id}/photos", queryParameters: {
      "page": page,
      "per_page": limit
    }).then((d) {
      var results = new NetworkCommon().decodeResp(d);
      Page page = new NetworkCommon().decodePage(d);
      page.data =
          results.map((item) => new Photo.fromJson(item)).toList();
      return page;
    });
  }

创建CollectionView

打开插件,输入页面名Collection,选择UI only,Query,Listview。输入Photo到Model Entry Name。生成页面。
编辑collection_View.dart

// add a int property in CollectionView to specify collection id
  final int collection;
  
  edit widget
    widget = NotificationListener(
        onNotification: _onNotification,
        child: RefreshIndicator(
            key: _refreshIndicatorKey,
            onRefresh: _handleRefresh,
            child: new StaggeredGridView.countBuilder(
              controller: _scrollController,
              crossAxisCount: 2,
              itemCount: this.widget.viewModel.photos.length + 1,
              itemBuilder: (_, int index) => _createItem(context, index),
              staggeredTileBuilder: (int index) => new StaggeredTile.fit(1),
              mainAxisSpacing: 0.0,
              crossAxisSpacing: 0.0,
            )));

// modify list item
  _createItem(BuildContext context, int index) {
    if (index < this.widget.viewModel.photos?.length) {
      return Container(
          padding: EdgeInsets.all(2.0),
          child: Stack(
            children: [
              Hero(
                tag: this.widget.viewModel.photos[index].id,
                child: InkWell(
                  onTap: () => Navigator.push(
                        context,
                        MaterialPageRoute(
                          builder: (context) => ViewPhotoView(
                              id: this.widget.collection, pageIndex: index),
                        ),
                      ),
                  child: new CachedNetworkImage(
                    imageUrl: this.widget.viewModel.photos[index].urls.small,
                    placeholder: (context, url) =>
                        new CircularProgressIndicator(),
                    errorWidget: (context, url, error) => new Icon(Icons.error),
                  ),
                ),
              ),
            ],
          ),
          decoration: BoxDecoration(
              border: Border(
                  bottom: BorderSide(color: Theme.of(context).dividerColor))));
    }

    return Container(
      height: 44.0,
      child: Center(
        child: _getLoadMoreWidget(),
      ),
    );
  }

修改collection_view_model.dart

  final Function(bool) getPhotoOfCollection;
  
  static CollectionViewModel fromStore(Store store, int id) {
    return CollectionViewModel(
      photos: store.state.photoState.collectionPhotos[id]?.photos?.values
          ?.toList() ?? [],
      getPhotoOfCollection: (isRefresh) {
        store.dispatch(GetCollectionPhotosAction(id: id, isRefresh: isRefresh));
      },

添加一些collection 到 Discover page

  List _tabs = [
    "Latest",
    "Wallpapers",
    "Textures",
    "Rainy",
    "Summer",
    "Flowers",
    "Women",
    "Home",
    "Oh Baby","Work","Winter","Animals"
  ];
  List _views = [
    0,
    151521,
    175083,
    1052192,
    583479,
    1988224,
    4386752,
    145698,
    1099399,385548,3178572,181581
  ];

或者,通过接口从服务器获取

  List getTabPPage() {
    List list = [];
    list.add(0);
    for (var c in this.widget.viewModel.collections) {
      list.add(c.id);
    }

    return list;
  }

  List getTab() {
    List list = [];
    list.add("latest");
    for (var c in this.widget.viewModel.collections) {
      list.add(c.title ?? "");
    }

    return list;
  }

创建photo Viewer page

打开插件,输入ViewPhoto,选择UI only,输入Photo作为Model Entry Name,生成。
Add two property to ViewPhotoView

  final int id; //collection id
  final int pageIndex; //the index of photo in the list
  
  // build()
      widget = Container(
        child: PhotoViewGallery.builder(
      scrollPhysics: const BouncingScrollPhysics(),
      builder: (BuildContext context, int index) {
        return PhotoViewGalleryPageOptions(
          imageProvider: CachedNetworkImageProvider(
              this.widget.viewModel.photos[index].urls.small),
          initialScale: PhotoViewComputedScale.contained * 0.8,
          heroTag: this.widget.viewModel.photos[this.widget.pageIndex ?? 0].id,
        );
      },
      itemCount: this.widget.viewModel.photos.length,
      loadingChild: new CircularProgressIndicator(),
      pageController: _pc,
    ));

编辑view_photo_view_model.dart,添加数据源

    return ViewPhotoViewModel(
      photos: id == 0
          ? store.state.photoState.photos.values.toList() ?? []
          : store.state.photoState.collectionPhotos[id]?.photos?.values
                  ?.toList() ??
              [],
    );

添加点击时间到photo列表项:collection_view.dart and photo_view.dart

                child: InkWell(
                  onTap: () => Navigator.push(
                        context,
                        MaterialPageRoute(
                          builder: (context) => ViewPhotoView(
                              id: this.widget.collection, pageIndex: index),
                        ),
                      ),

添加 Setting page到Me page
编辑me_view.dart

    widget = RaisedButton(
      child: Text("Settings"),
      onPressed: () => Navigator.of(context).pushNamed("/settings"),
    );

到此,一个应用的雏形已经呈现出来了,如此简单。
Check the source code for the detail.

使用Flutter App Template Generater快速开发应用_第4张图片
discover

你可能感兴趣的:(使用Flutter App Template Generater快速开发应用)