美文网首页Flutter圈子Flutter中文社区Flutter
使用Flutter App Template Generater

使用Flutter App Template Generater

作者: aduo | 来源:发表于2019-05-07 16:50 被阅读9次

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

    discover dark
    collection list dark
    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<String, WidgetBuilder> _routes() {
       return <String, WidgetBuilder>{
         "/settings": (_) => SettingsOptionsPage(
               options: _options,
               onOptionsChanged: _handleOptionsChanged,
             ),
         "/": (_) => new HomeView(),
       };
     }
    

    home_view.dart

       widget = PageView(
           children: <Widget>[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<Page> 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<Photo>((item) => new Photo.fromJson(item)).toList();
          return page;
        });
      }
    

    修改中间件

    编辑photo_middleware.dart

    Middleware<AppState> _createGetPhotos(PhotoRepository repository) {
      return (Store<AppState> 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: <Widget>[
                  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<String> _tabs = [
        "Latest",
     //will add some photos of some collection here
      ];
      List<int> _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<int, PhotoOfCollection> 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<PhotoState, SyncCollectionPhotosAction>(_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<AppState, GetCollectionPhotosAction>(getCollectionPhotos),
    
    // new handle function
    Middleware<AppState> _createGetCollectionPhotos(PhotoRepository repository) {
      return (Store<AppState> 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<Page> 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<Photo>((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: <Widget>[
                  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<AppState> 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<String> _tabs = [
        "Latest",
        "Wallpapers",
        "Textures",
        "Rainy",
        "Summer",
        "Flowers",
        "Women",
        "Home",
        "Oh Baby","Work","Winter","Animals"
      ];
      List<int> _views = [
        0,
        151521,
        175083,
        1052192,
        583479,
        1988224,
        4386752,
        145698,
        1099399,385548,3178572,181581
      ];
    

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

      List<int> getTabPPage() {
        List<int> list = [];
        list.add(0);
        for (var c in this.widget.viewModel.collections) {
          list.add(c.id);
        }
    
        return list;
      }
    
      List<String> getTab() {
        List<String> 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.

    discover

    相关文章

      网友评论

        本文标题:使用Flutter App Template Generater

        本文链接:https://www.haomeiwen.com/subject/vtieoqtx.html