最近写前端,没有总结flutter的学习进程,现在来总结一下,关于动画,布局,登录,首页,路由,json格式化,下拉刷新,图片加载预览,欢迎页,焦点问题,保活,上传图片,页面信息传递,这些知识点。由于官方文档给的知识点很少,大部分是一些零零散散的API,所以只能通过一些大神的总结去剥离功能性的写法,如果理解不了的话就google,记不住的话就看demo,写多了就记住了,前期国内资源有限大家可以慢慢的学,但是要学,如果你还想做APP的话,flutter以后肯定会有一片天地的,毕竟一套代码三个平台,企业老板会因为成本多多少少青睐的,所以加油~

    附:效果图因为demo有点多,会慢慢补齐,写了三个小时,感谢大家支持

    1.animated_container(放大 缩小github:https://github.com/geeklx/flutter_app2/tree/master/app2/animated_container):AnimationContainer使用要点,必须传入Duration告诉动画的播放时间,当animationContainer接收到一个新的值的时候,会根据老值进行补间动画,例如开始宽高为100,然后给了新值0并setState后,AnimationContainer会让宽高从100逐渐变化到0,其中变化曲线由Curve决定,默认为Curves.linear。其实动画大部分都是封装好的,多写写就会了,实在记不住就看demo,好记性不如个烂笔头。

blob.png blob.png

import 'package:flutter/material.dart';
import 'animated_container_demo.dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: AnimatedContainerDemo(),
    );
  }
}


/**
 * AnimationContainer使用要点
 * 必须传入Duration告诉动画的播放时间
 * 当animationContainer接收到一个新的值的时候
 * 会根据老值进行补间动画
 * 例如开始宽高为100,然后给了新值0并setState后
 * AnimationContainer会让宽高从100逐渐变化到0
 * 其中变化曲线由Curve决定,默认为Curves.linear
 */
import 'package:flutter/material.dart';

class AnimatedContainerDemo extends StatefulWidget {
  @override
  _AnimatedContainerDemoState createState() => _AnimatedContainerDemoState();
}

class _AnimatedContainerDemoState extends State<AnimatedContainerDemo> {
  double _value = 255.0;

  _changeValue() => setState(() {
        _value = _value == 255.0 ? 80.0 : 255.0;
        print(_value);
      });

  @override
  Widget build(BuildContext context) {
    return Center(
      child: GestureDetector(
        onTap: _changeValue, //放大 缩小
        child: AnimatedContainer(
          curve: Curves.decelerate,
          duration: Duration(seconds: 1),
          width: _value,
          height: _value,
          child: FlutterLogo(),
        ),
      ),
    );
  }
}

    2.animated_cross_fade(横竖转向动画github:https://github.com/geeklx/flutter_app2/tree/master/app2/animated_cross_fade):

blob.png blob.png

import 'package:flutter/material.dart';

class AnimatedCrossFadeDemo extends StatefulWidget {
  @override
  _AnimatedCrossFadeDemoState createState() => _AnimatedCrossFadeDemoState();
}

class _AnimatedCrossFadeDemoState extends State<AnimatedCrossFadeDemo> {
  bool _first = false;

  change() {
    setState(() {
      _first = _first ? false : true;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: GestureDetector(
        onTap: change, // 横竖转向动画
        child: AnimatedCrossFade(
          duration: const Duration(seconds: 2),
          firstChild: const FlutterLogo(
            style: FlutterLogoStyle.horizontal,
            size: 200.0,
          ),
          secondChild: const FlutterLogo(
            style: FlutterLogoStyle.stacked,
            size: 200.0,
          ),
          crossFadeState:
              _first ? CrossFadeState.showFirst : CrossFadeState.showSecond,
        ),
      ),
    );
  }
}

    3.animated_floating_action_bar(悬浮按钮github:https://github.com/geeklx/flutter_app2/tree/master/app2/animated_floating_action_bar):这个是官方的demo

 blob.png

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        // This is the theme of your application.
        //
        // Try running your application with "flutter run". You'll see the
        // application has a blue toolbar. Then, without quitting the app, try
        // changing the primarySwatch below to Colors.green and then invoke
        // "hot reload" (press "r" in the console where you ran "flutter run",
        // or simply save your changes to "hot reload" in a Flutter IDE).
        // Notice that the counter didn't reset back to zero; the application
        // is not restarted.
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  // This widget is the home page of your application. It is stateful, meaning
  // that it has a State object (defined below) that contains fields that affect
  // how it looks.

  // This class is the configuration for the state. It holds the values (in this
  // case the title) provided by the parent (in this case the App widget) and
  // used by the build method of the State. Fields in a Widget subclass are
  // always marked "final".

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      // This call to setState tells the Flutter framework that something has
      // changed in this State, which causes it to rerun the build method below
      // so that the display can reflect the updated values. If we changed
      // _counter without calling setState(), then the build method would not be
      // called again, and so nothing would appear to happen.
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    // This method is rerun every time setState is called, for instance as done
    // by the _incrementCounter method above.
    //
    // The Flutter framework has been optimized to make rerunning build methods
    // fast, so that you can just rebuild anything that needs updating rather
    // than having to individually change instances of widgets.
    return Scaffold(
      appBar: AppBar(
        // Here we take the value from the MyHomePage object that was created by
        // the App.build method, and use it to set our appbar title.
        title: Text(widget.title),
      ),
      body: Center(
        // Center is a layout widget. It takes a single child and positions it
        // in the middle of the parent.
        child: Column(
          // Column is also layout widget. It takes a list of children and
          // arranges them vertically. By default, it sizes itself to fit its
          // children horizontally, and tries to be as tall as its parent.
          //
          // Invoke "debug painting" (press "p" in the console, choose the
          // "Toggle Debug Paint" action from the Flutter Inspector in Android
          // Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
          // to see the wireframe for each widget.
          //
          // Column has various properties to control how it sizes itself and
          // how it positions its children. Here we use mainAxisAlignment to
          // center the children vertically; the main axis here is the vertical
          // axis because Columns are vertical (the cross axis would be
          // horizontal).
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

    4.animation_challenge(github:https://github.com/geeklx/flutter_app2/tree/master/app2/animation_challenge):

blob.png

    home: HeroDemo(),// 添加list item

import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart' show timeDilation;

class HeroDemo extends StatefulWidget {
  @override
  _HeroDemoState createState() => _HeroDemoState();
}

class _HeroDemoState extends State<HeroDemo> {
  List<String> list;

  @override
  void initState() {
    super.initState();
    list = List.generate(20, (index) => "This is no.$index");
  }

  @override
  Widget build(BuildContext context) {
    timeDilation = 2.0;
    return Scaffold(
      appBar: AppBar(
        title: Text('Demo1'),
      ),
      body: ListView.builder(
          itemCount: list.length,
          itemBuilder: (context, index) {
            return ListTile(
              title: Text(list[index]),
            );
          }),
      floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
      floatingActionButton: Hero(
        tag: "FloatingActionButton",
        child: FloatingActionButton(
          backgroundColor: Colors.blue,
          onPressed: () => Navigator.of(context)
              .push(MaterialPageRoute(builder: (context) => SecondPage())),
          child: Icon(Icons.add),
        ),
      ),
    );
  }
}

class SecondPage extends StatefulWidget {
  @override
  _SecondPageState createState() => _SecondPageState();
}

class _SecondPageState extends State<SecondPage> {
  final FocusNode focusNode = FocusNode();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.transparent,
        elevation: 0.0,
        iconTheme: IconThemeData(
          color: Colors.black,
        ),
      ),
      body: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        mainAxisSize: MainAxisSize.max,
        children: <Widget>[
          ListView(
            shrinkWrap: true,
            padding: const EdgeInsets.symmetric(horizontal: 20.0),
            children: <Widget>[
              TextField(
                autofocus: true,
                focusNode: focusNode,
                maxLines: 5,
                decoration: InputDecoration.collapsed(
                    hintText: 'What do you want to add now ?'),
              ),
            ],
          ),
        ],
      ),
      floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
      floatingActionButton: Hero(
          tag: "FloatingActionButton",
          child: Padding(
            padding:
                const EdgeInsets.only(left: 12.0, right: 12.0, bottom: 6.0),
            child: ButtonTheme(
              height: 48.0,
              minWidth: double.infinity,
              child: RaisedButton(
                color: Colors.lightBlue,
                onPressed: () {},
                elevation: 10.0,
                child: Icon(
                  Icons.add,
                  color: Colors.white,
                ),
              ),
            ),
          )),
    );
  }
}

    home: HideBottomBarDemo(),// 滚动隐藏底部导航 动画

import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';

class HideBottomBarDemo extends StatefulWidget {
  @override
  _HideBottomBarDemoState createState() => _HideBottomBarDemoState();
}

class _HideBottomBarDemoState extends State<HideBottomBarDemo>
    with SingleTickerProviderStateMixin {
  AnimationController _animationController;
  Animation _animation;
  ScrollController _scrollController;

  void _judgeScroll() {
    if (_scrollController.position.userScrollDirection ==
        ScrollDirection.reverse) {
      _animationController.forward();
    }
    if (_scrollController.position.userScrollDirection ==
        ScrollDirection.forward) {
      _animationController.reverse();
    }
  }

  @override
  void initState() {
    super.initState();
    _animationController =
        AnimationController(vsync: this, duration: Duration(milliseconds: 300));
    _animation = Tween(begin: 0.0, end: -100.0).animate(CurvedAnimation(
        parent: _animationController, curve: Curves.fastOutSlowIn));
    _scrollController = ScrollController(keepScrollOffset: true)
      ..addListener(_judgeScroll);
  }

  @override
  void dispose() {
    // TODO: implement dispose
    _animationController?.dispose();
    _scrollController..removeListener(_judgeScroll);
    // 如果销毁ScrollController,keepScrollOffset将置空
    // _scrollController?.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Immersive BottomNavigationBar'),
      ),
      body: _buildListView(),
      bottomNavigationBar: _buildBottomNavigationBar(context),
    );
  }

  Widget _buildBottomNavigationBar(BuildContext context) {
    return AnimatedBuilder(
      animation: _animation,
      builder: (context, child) {
        return Container(
          height: 0.0,
          child: Stack(
            overflow: Overflow.visible,
            children: <Widget>[
              Positioned(
                  bottom: _animation.value, left: 0.0, right: 0.0, child: child)
            ],
          ),
        );
      },
      child: BottomNavigationBar(
        items: [
          BottomNavigationBarItem(icon: Icon(Icons.title), title: Text("home")),
          BottomNavigationBarItem(icon: Icon(Icons.title), title: Text("home")),
          BottomNavigationBarItem(icon: Icon(Icons.title), title: Text("home")),
          BottomNavigationBarItem(icon: Icon(Icons.title), title: Text("home")),
        ],
        type: BottomNavigationBarType.fixed,
      ),
    );
  }

  Widget _buildListView() => ListView.builder(
      controller: _scrollController,
      itemBuilder: (context, index) => ListTile(
            leading: Icon(Icons.access_alarm),
            title: Text("this is index: $index"),
          ));

}

    home: AudioScreen(),// 倒计时 暂时不好写 没理解(官方demo需要后续去看看写法很巧妙)

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';

class AudioScreen extends StatefulWidget {
  @override
  _AudioScreenState createState() => _AudioScreenState();
}

class _AudioScreenState extends State<AudioScreen> {
  Stopwatch stopwatch = Stopwatch();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        color: Colors.orangeAccent.withOpacity(0.2),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            _buildRecordingStatus(),
            _buildTimerText(),
            _buildButtonRow(context),
          ],
        ),
      ),
    );
  }

  void _stopButtonPressed() {
    setState(() {
      stopwatch
        ..stop()
        ..reset();
    });
  }

  void _rightButtonPressed() {
    setState(() {
      if (stopwatch.isRunning) {
        stopwatch.stop();
      } else {
        stopwatch.start();
      }
    });
  }

  Widget _buildTimerText() {
    return Container(
        height: 200.0,
        child: Center(
          child: TimerText(stopwatch: stopwatch),
        ));
  }

  Widget _buildRecordingStatus() {
    return Container(
        height: 100.0,
        width: 100.0,
        child: stopwatch.isRunning
            ? Center(
                child: SpinKitWave(
                    color: Colors.black87.withOpacity(0.7),
                    type: SpinKitWaveType.start),
              )
            : Image.asset("assets/recorder.png"));
  }

  Widget _buildButtonRow(BuildContext context) {
    return Row(children: <Widget>[
      _buildButton(_stopButtonPressed, Colors.redAccent, context, Icons.stop),
      _buildButton(_rightButtonPressed, Colors.blueAccent, context,
          stopwatch.isRunning ? Icons.pause : Icons.play_arrow),
    ]);
  }

  Widget _buildButton(
      VoidCallback callback, Color color, BuildContext context, IconData icon) {
    Size size = MediaQuery.of(context).size;
    return Container(
        width: size.width * 0.5,
        alignment: Alignment.center,
        child: RaisedButton(
          elevation: 0.0,
          shape:
              RoundedRectangleBorder(borderRadius: BorderRadius.circular(36.0)),
          color: color,
          onPressed: callback,
          child: Container(
            width: size.width * 0.5 - 80.0,
            height: MediaQuery.of(context).size.width * 0.15,
            child: Icon(
              icon,
              color: Colors.white,
              size: 32.0,
            ),
          ),
        ));
  }
}

class TimerText extends StatefulWidget {
  TimerText({this.stopwatch});
  final Stopwatch stopwatch;

  TimerTextState createState() => TimerTextState(stopwatch: stopwatch);
}

class TimerTextState extends State<TimerText> {
  Timer timer;
  final Stopwatch stopwatch;

  TimerTextState({this.stopwatch}) {
    timer = Timer.periodic(Duration(milliseconds: 30), callback);
  }

  void callback(Timer timer) {
    if (stopwatch.isRunning) {
      setState(() {});
    }
  }

  @override
  Widget build(BuildContext context) {
    final double width = MediaQuery.of(context).size.width;
    final TextStyle timerTextStyle = const TextStyle(
      fontSize: 60.0,
      fontFamily: "Open Sans",
      fontWeight: FontWeight.w300,
      color: Colors.black87,
    );
    List<String> formattedTime =
        TimerTextFormatter.format(stopwatch.elapsedMilliseconds);
    return Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        Container(
          child: Text(
            "${formattedTime[0]}:",
            style: timerTextStyle,
          ),
          width: width / 4.0,
        ),
        Container(
          child: Text(
            "${formattedTime[1]}:",
            style: timerTextStyle,
          ),
          width: width / 4.1,
        ),
        Container(
          child: Text(
            "${formattedTime[2]}",
            style: timerTextStyle,
          ),
          width: width / 4.6,
        ),
      ],
    );
  }
}

class TimerTextFormatter {
  static List<String> format(int milliseconds) {
    int hundreds = (milliseconds / 10).truncate();
    int seconds = (hundreds / 100).truncate();
    int minutes = (seconds / 60).truncate();

    String minutesStr = (minutes % 60).toString().padLeft(2, '0');
    String secondsStr = (seconds % 60).toString().padLeft(2, '0');
    String hundredsStr = (hundreds % 100).toString().padLeft(2, '0');

    return [minutesStr, secondsStr, hundredsStr];
//    return "$minutesStr:$secondsStr:$hundredsStr";
  }
}

    home: ImScreen(),// IM 聊天 这里需要注意的是listview滚动会因为item的增加卡顿,目前布局这块没有好的方案,期待官方。

import 'dart:async';

import 'package:flutter/material.dart';

class ImScreen extends StatefulWidget {
  @override
  _ImScreenState createState() => _ImScreenState();
}

class _ImScreenState extends State<ImScreen> {
  StreamController _messageController;
  TextEditingController _textController;
  List _myMessages;
  final _myName = "Vadaski";

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    _messageController = StreamController();
    _textController = TextEditingController();
    _myMessages = List();
  }

  @override
  void dispose() {
    _textController.dispose();
    _messageController.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('IM Challenge'),
        centerTitle: true,
      ),
      body: SafeArea(
        child: Column(
          children: <Widget>[
            Flexible(
                child: ListView.builder(
                    reverse: true,
                    itemCount: _myMessages.length,
                    itemBuilder: (context, index) {
                      return _buildMessageWidget(_myMessages[index], context);
                    })),
            Divider(
              height: 1.0,
            ),
            _buildInputWidget(context),
          ],
        ),
      ),
    );
  }

  Widget _buildInputWidget(BuildContext context) {
    return SizedBox(
      child: Padding(
        padding: const EdgeInsets.only(left: 8.0, right: 8.0),
        child: Row(
          children: <Widget>[
            Flexible(
                child: TextField(
              decoration:
                  InputDecoration.collapsed(hintText: "Send your message"),
              controller: _textController,
              onChanged: onMessageChanged,
              onSubmitted: onMessageSubmit,
            )),
            Container(
              margin: const EdgeInsets.symmetric(horizontal: 4.0),
              child: StreamBuilder(
                initialData: "",
                stream: _messageController.stream,
                builder: (context, snapshot) {
                  return IconButton(
                    icon: Icon(
                      Icons.send,
                      color: snapshot.data == ""
                          ? Colors.grey
                          : Theme.of(context).accentColor,
                    ),
                    onPressed: () => onMessageSubmit(_textController.text),
                  );
                },
              ),
            )
          ],
        ),
      ),
    );
  }

  Widget _buildMessageWidget(String text, BuildContext context) {
    return Container(
      margin: const EdgeInsets.symmetric(vertical: 10.0),
      width: MediaQuery.of(context).size.width / 2,
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          SizedBox(
            width: MediaQuery.of(context).size.width / 4,
          ),
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.end,
              children: <Widget>[
                Text(_myName, style: Theme.of(context).textTheme.subhead),
                Container(
                  decoration: BoxDecoration(
                      borderRadius: BorderRadius.circular(12.0),
                      color: Colors.blue.withOpacity(0.2)),
                  margin: const EdgeInsets.only(top: 5.0),
                  child: Padding(
                    padding: const EdgeInsets.all(8.0),
                    child: Text(
                      text,
                      overflow: TextOverflow.fade,
                      softWrap: true,
                    ),
                  ),
                )
              ],
            ),
          ),
          Container(
            margin: const EdgeInsets.only(right: 16.0, left: 8.0),
            child: CircleAvatar(
              child: Text(_myName[0]),
            ),
          ),
        ],
      ),
    );
  }

  onMessageChanged(String message) {
    _messageController.sink.add(message);
  }

  onMessageSubmit(String message) {
    _textController.clear();
    if (message != "") {
      setState(() {
        _myMessages.insert(0, message);
      });
    }
    onMessageChanged("");
  }
}

    home: RotatingScreen(),//

import 'package:flutter/material.dart';

import '../widgets/rotating_bar.dart';

class RotatingScreen extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
   final size = MediaQuery.of(context).size;

   return Scaffold(
       body: Stack(
     children: <Widget>[
       Positioned(
         left: size.width / 5,
         top: size.height / 3,
         child: RotatingBar(
           getBackCenter: true,
           dx: size.width / 5,
           dy: size.height / 3,
           style: Style.Touch,
           getAngle: (angle) {
             print(angle);
           },
         ),
       )
     ],
   ));
 }
}

    home: ScrollBackToTop(),// 滚动到顶部 这个比较常用

import 'package:flutter/material.dart';

class ScrollBackToTop extends StatefulWidget {
 @override
 _ScrollBackToTopState createState() => _ScrollBackToTopState();
}

class _ScrollBackToTopState extends State<ScrollBackToTop>
   with SingleTickerProviderStateMixin {
 ScrollController _controller;

 @override
 void initState() {
   super.initState();
   _controller = ScrollController();
 }

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

 @override
 Widget build(BuildContext context) {
   return Scaffold(
     appBar: AppBar(
       title: Text('Scroll Back To Top Demo'),
       centerTitle: true,
     ),
     body: ListView.builder(
         controller: _controller,
         itemCount: 100,
         itemBuilder: (context, index) {
           return ListTile(
             title: Center(
                 child: Text(
               'This is no $index',
               style: TextStyle(fontSize: 24),
             )),
           );
         }),
     floatingActionButton: FloatingActionButton(
       onPressed: backToTop,
       child: Icon(Icons.vertical_align_top),
     ),
   );
 }

 backToTop() {
   if (_controller.offset != 0)
     _controller.animateTo(
       0,
       duration: Duration(milliseconds: 500),
       curve: Curves.easeIn,
     );
 }
}

    5.animation_demo(总结一下各种目前能用的场景化动画github:https://github.com/geeklx/flutter_app2/tree/master/app2/animation_demo):由于篇幅有限代码就不说了,地址附上 blob.png blob.png

    6.beaytiful_search_bar_demo(搜索栏):https://github.com/geeklx/flutter_app2/tree/master/app2/beaytiful_search_bar_demo

blob.png

import 'package:flutter/material.dart';
import 'asset.dart';

class SearchBarDemo extends StatefulWidget {
 @override
 _SearchBarDemoState createState() => _SearchBarDemoState();
}

class _SearchBarDemoState extends State<SearchBarDemo> {
 @override
 Widget build(BuildContext context) {
   return Scaffold(
     appBar: AppBar(
       title: Text('SearchBarDemo'),
       actions: <Widget>[
         IconButton(
             icon: Icon(Icons.search),
             onPressed: () =>
                 showSearch(context: context, delegate: SearchBarDelegate())),
       ],
     ),
   );
 }
}

const searchList = [
 "ChengDu",
 "ShangHai",
 "BeiJing",
 "TianJing",
 "NanJing",
 "ShenZheng"
];

const recentSuggest = [
 "suggest1",
 "suggest2"
];

class SearchBarDelegate extends SearchDelegate<String> {
 @override
 List<Widget> buildActions(BuildContext context) {
   return [IconButton(icon: Icon(Icons.clear), onPressed: () => query = "")];
 }

 @override
 Widget buildLeading(BuildContext context) {
   return IconButton(
       icon: AnimatedIcon(
           icon: AnimatedIcons.menu_arrow, progress: transitionAnimation),
       onPressed: () => close(context, null));
 }

 @override
 Widget buildResults(BuildContext context) {
   return Center(child: Container(
     width: 100.0,
     height: 100.0,
     child: Card(
       color: Colors.redAccent,
       child: Center(
         child: Text(query),
       ),
     ),
   ),);
 }

 @override
 Widget buildSuggestions(BuildContext context) {
   final suggestionList = query.isEmpty
       ? recentSuggest
       : searchList.where((input) => input.startsWith(query)).toList();
   return ListView.builder(
       itemCount: suggestionList.length,
       itemBuilder: (context, index) => ListTile(

         onTap: (){
           query = suggestionList[index];
           showResults(context);},

             title: RichText(
                 text: TextSpan(
                     text: suggestionList[index].substring(0, query.length),
                     style: TextStyle(
                         color: Colors.black, fontWeight: FontWeight.bold),
                     children: [
                   TextSpan(
                       text: suggestionList[index].substring(query.length),
                       style: TextStyle(color: Colors.grey))
                 ])),
           ));
 }
}

    7.bloc_provider_pattern(监听私有方法调用写法):https://github.com/geeklx/flutter_app2/tree/master/app2/bloc_provider_pattern

blob.png

import 'dart:async';

import './bloc_base.dart';

class IncrementBloc implements BlocBase {
 int _counter;

 StreamController<int> _counterPipe = StreamController<int>();
 Stream<int> get outCounter => _counterPipe.stream;

 IncrementBloc() {
   _counter = 0;
   //_counterPipe 用于StreamBuilder,已经自动加了listen
 }

 incrementCounter() {
   _counter = _counter + 1;
   _counterPipe.sink.add(_counter);
 }

 void dispose() {
   print('bloc disposed!');
   _counterPipe.close();
 }
}


import 'package:flutter/material.dart';

// Generic Interface for all BLoCs
abstract class BlocBase {
 void dispose();
}

// Generic BLoC provider
class BlocProvider<T extends BlocBase> extends StatefulWidget {
 BlocProvider({
   Key key,
   @required this.child,
   @required this.bloc,
 }) : super(key: key);

 final T bloc;
 final Widget child;

 @override
 _BlocProviderState<T> createState() => _BlocProviderState<T>();

 static T of<T extends BlocBase>(BuildContext context) {
   final type = _typeOf<BlocProvider<T>>();
   BlocProvider<T> provider = context.ancestorWidgetOfExactType(type);
   return provider.bloc;
 }

 static Type _typeOf<T>() => T;
}

class _BlocProviderState<T> extends State<BlocProvider<BlocBase>> {
 @override
 void dispose() {
   widget.bloc.dispose();
   super.dispose();
 }

 @override
 Widget build(BuildContext context) {
   return widget.child;
 }
}
//EOP

    8.bottom_appbar_demo(底部导航 比较常用的base写法):https://github.com/geeklx/flutter_app2/tree/master/app2/bottom_appbar_demo

blob.png

import 'package:flutter/material.dart';

class EachView extends StatefulWidget {
 String _title;
 EachView(this._title);
 @override
 _EachViewState createState() => _EachViewState();
}

class _EachViewState extends State<EachView> {
 @override
 Widget build(BuildContext context) {
   return Scaffold(
     appBar: AppBar(title: Text(widget._title),),
   );
 }
}

import 'package:flutter/material.dart';
import 'each_view.dart';

class BottomAppBarDemo extends StatefulWidget {
 @override
 _BottomAppBarDemoState createState() => _BottomAppBarDemoState();
}

class _BottomAppBarDemoState extends State<BottomAppBarDemo> {
 List<Widget> _eachView;
 int _index = 0;

 @override
 void initState() {
   // TODO: implement initState
   super.initState();
   _eachView = List();
   _eachView..add(EachView('home'))..add(EachView('me'));
 }

 @override
 Widget build(BuildContext context) {
   return new Scaffold(
     body: _eachView[_index],
     floatingActionButton: new FloatingActionButton(
       onPressed: () {
         Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context){
           return EachView('New Page');
         }));
       },
       tooltip: 'Increment',
       child: new Icon(Icons.add),
     ), // T
     floatingActionButtonLocation:
         FloatingActionButtonLocation.centerDocked, // his
     bottomNavigationBar: BottomAppBar(
       color: Colors.lightBlue,
       shape: CircularNotchedRectangle(),
       child: Row(
         mainAxisSize: MainAxisSize.max,
         mainAxisAlignment: MainAxisAlignment.spaceAround,
         children: <Widget>[
           IconButton(
             icon: Icon(Icons.near_me),
             color: Colors.white,
             onPressed: () {
               setState(() {
                 _index = 0;
               });
             },
           ),
           IconButton(
             icon: Icon(Icons.edit_location),
             color: Colors.white,
             onPressed: () {
               setState(() {
                 _index = 1;
               });
             },
           ),
         ],
       ),
     ),
   );
 }
}

    9.chip_demo(勾选择view的各种效果):https://github.com/geeklx/flutter_app2/tree/master/app2/chip_demo

blob.png

import 'package:chip_demo/input_chip.dart';
/**
* 请通过切换home的注释分别查看
*/
import 'package:flutter/material.dart';

import 'choice_chip.dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
   return new MaterialApp(
     title: 'Flutter Demo',
     theme: new ThemeData.light(),
//      home: ChipDemo(),
//      home: ActionChipDemo(),
//      home: FilterChipDemo(),// 勾选状态
//      home: ChoiceChipDemo(), // 选中状态 常用的
     home: InputChipDemo(),
   );
 }
}

    10.custom_router_transition(标准的路由写法):https://github.com/geeklx/flutter_app2/tree/master/app2/custom_router_transition

/**
* Navigator.of(context).push自定义的route
*/
import 'package:flutter/material.dart';
import 'custome_router.dart';

class FirstPage extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
   return Scaffold(
     backgroundColor: Colors.blue,
     appBar: AppBar(
       title: Text(
         'FirstPage',
         style: TextStyle(fontSize: 36.0),
       ),
       elevation: 0.0,
     ),
     body: Center(
       child: MaterialButton(
           child: Icon(
             Icons.navigate_next,
             color: Colors.white,
             size: 64.0,
           ),
           onPressed: () =>
               Navigator.of(context).push(
                   CustomRoute(SecondPage()))),
     ),
   );
 }
}

class SecondPage extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
   return Scaffold(
     backgroundColor: Colors.pinkAccent,
     appBar: AppBar(
       title: Text('SecondPage',style: TextStyle(fontSize: 36.0),),
       backgroundColor: Colors.pinkAccent,
       leading: Container(),
       elevation: 0.0,
     ),
     body: Center(
       child: MaterialButton(
           child: Icon(
             Icons.navigate_before,
             color: Colors.white,
             size: 64.0,
           ),
           onPressed: () => Navigator.of(context).pop()),
     ),
   );
 }
}


提供几个过度动画
/**
* 通过自定义transitionsBuilder实现路由过渡动画
*
* 请切换不同注释分别查看
*/
import 'package:flutter/material.dart';

class CustomRoute extends PageRouteBuilder {
 final Widget widget;
 CustomRoute(this.widget)
     : super(
         transitionDuration: const Duration(seconds: 2),
         pageBuilder: (BuildContext context, Animation<double> animation,
             Animation<double> secondaryAnimation) {
           return widget;
         },
         transitionsBuilder: (BuildContext context,
             Animation<double> animation,
             Animation<double> secondaryAnimation,
             Widget child) {
           //淡出过渡路由
         return FadeTransition(
             opacity: Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation(
                 parent: animation, curve: Curves.fastOutSlowIn)),
             child: child,
             );

           //比例转换路由
//          return ScaleTransition(
//            scale: Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation(
//                parent: animation, curve: Curves.fastOutSlowIn)),
//            child: child,
//            );

           //旋转+比例转换路由
//            return RotationTransition(
//              turns: Tween(begin: -1.0, end: 1.0).animate(CurvedAnimation(
//                  parent: animation, curve: Curves.fastOutSlowIn)),
//              child: ScaleTransition(
//                scale: Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation(
//                    parent: animation, curve: Curves.fastOutSlowIn)),
//                child: child,
//              ),
//            );

           //幻灯片路由
//            return SlideTransition(
//              position:
//                  Tween<Offset>(begin: Offset(0.0, -1.0), end: Offset(0.0, 0.0))
//                      .animate(CurvedAnimation(
//                          parent: animation, curve: Curves.fastOutSlowIn)),
//              child: child,
//            );
         },
       );
}

    11.event_bus_demo(利用eventbus同步页面数据局部刷新 比较常用):https://github.com/geeklx/flutter_app2/tree/master/app2/event_bus_demo

import 'package:flutter/material.dart';
import 'tools/bus.dart';
import 'events/count_events.dart';
import 'sceeens/first_screen.dart';

void main(){
 runApp(App());
 behaviorBus.fire(CountEvent(0));
}

class App extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
   return MaterialApp(
     title: 'Flutter Demo',
     theme: ThemeData.dark(),
     home: FirstScreen(),

   );
 }
}

    12.expansion_demo(级联listview 目前这块官方没有demo 只找到郭神的一个写法 视图刷新很卡 目前也只能这样 后续跟进):https://github.com/geeklx/flutter_app2/tree/master/app2/expansion_demo

blob.png

1.
/**
 *最基础的展开小部件expansion tile
 * 用法很简单,将需要被展开的部件放在children中即可
 * 其他用法和list tile很相似
 * 当expansion tile 被展开时,我们可以看到background color
 * 会进行一个transition动画进行过渡
 * expansion tile还有一个trailing属性,代表右边的小箭头
 * 可以自行替换
 * initiallyExpanded代表最初的状态是否被展开
 * 默认为false,也就是不展开
 *
 * 当一个list view中由多个expansion tile的时候
 * 需要给每一个expansion tile指定唯一的[PageStorageKey]
 * 以保证在滑动的过程中,能够记住expansion tile的开关状态
 */

import 'package:flutter/material.dart';

class ExpansionTileDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('expansion tile demo'),),
      body: Center(
        child: ExpansionTile(
            title: Text('Expansion Tile'),
            leading: Icon(Icons.ac_unit),
            backgroundColor: Colors.white12,
            children: <Widget>[
              ListTile(
                title: Text('list tile'),
                subtitle: Text('subtitle'),
              ),
            ],
//          initiallyExpanded: true,
        ),
      ),
    );
  }
}

2.
/**
 *  说实话,我觉得expansion panel list一点都不好用
 *  所以不想写注释。
 *  这里样例代码来自于flutter开发者
 *  他会经常更新一些flutter教程,写的挺不错的,有兴趣自己去看看吧
 *  https://mp.weixin.qq.com/s/Qv08V42LgEr8IATUSfVVHg
 *
 *  需要注意几点:
 *  ExpansionPanelList必须放在可滑动组件中使用
 *  ExpansionPanel只能在ExpansionPanelList中使用
 *  除了ExpansionPanel还有一种特殊的ExpansionPanelRadio
 *  也是只能在ExpansionPanelList中使用的
 */

import 'package:flutter/material.dart';

class ExpansionPanelListDemo extends StatefulWidget {
  @override
  _ExpansionPanelListDemoState createState() => _ExpansionPanelListDemoState();
}

class _ExpansionPanelListDemoState extends State<ExpansionPanelListDemo> {
  var currentPanelIndex = -1;

  List<int> mList;
  //用来保存expansionPanel的状态
  List<ExpandStateBean> expandStateList;

  _ExpansionPanelListDemoState() {
    mList = new List();
    expandStateList = new List();
    for (int i = 0; i < 10; i++) {
      mList.add(i);
      expandStateList.add(ExpandStateBean(i, false));
    }
  }

  _setCurrentIndex(int index, isExpand) {
    setState(() {
      expandStateList.forEach((item) {
        if (item.index == index) {
          item.isOpen = !isExpand;
        }
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("expansion panel list"),
        ),
        body: SingleChildScrollView(
          child: ExpansionPanelList(
            expansionCallback: (index, bol) {
              _setCurrentIndex(index, bol);
            },
            children: mList.map((index) {
              return new ExpansionPanel(
                headerBuilder: (context, isExpanded) {
                  return new ListTile(
                    title: new Text('This is NO. $index'),
                  );
                },
                body: ListTile(
                  title: Text('expansion no.$index'),
                ),
                isExpanded: expandStateList[index].isOpen,
              );
            }).toList(),
          ),
        ));
  }
}

class ExpandStateBean {
  var isOpen;
  var index;
  ExpandStateBean(this.index, this.isOpen);
}

    13.flutter_bottomnavigationbar(底部导航标准写法支持保活):https://github.com/geeklx/flutter_app2/tree/master/app2/flutter_bottomnavigationbar

blob.png

1.
import 'package:flutter/material.dart';

class AirPlayScreen extends StatefulWidget {
  @override
  _AirPlayScreenState createState() => _AirPlayScreenState();
}

class _AirPlayScreenState extends State<AirPlayScreen>
    with AutomaticKeepAliveClientMixin {
  @override
  // TODO: implement wantKeepAlive
  bool get wantKeepAlive => true;

  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('AirPlayScreen'),
      ),
      body: new Center(
        child: new Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            new Text(
              'You have pushed the button this many times:',
            ),
            new Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),
      floatingActionButton: new FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: new Icon(Icons.add),
      ),
    );
  }
}

2.
import 'package:flutter/material.dart';
import 'package:flutter_bottomnavigationbar/pages_keep_alive/airplay_screen.dart';
import 'package:flutter_bottomnavigationbar/pages_keep_alive/email_screen.dart';
import 'package:flutter_bottomnavigationbar/pages_keep_alive/home_screen.dart';
import 'package:flutter_bottomnavigationbar/pages_keep_alive/pages_screen.dart';

class NavigationKeepAlive extends StatefulWidget {
  @override
  _NavigationKeepAliveState createState() => _NavigationKeepAliveState();
}

class _NavigationKeepAliveState extends State<NavigationKeepAlive>
    with SingleTickerProviderStateMixin {

  final _bottomNavigationColor = Colors.blue;
  int _currentIndex = 0;
  var _controller = PageController(
    initialPage: 0,
  );

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: PageView(
        controller: _controller,
        children: <Widget>[
          AirPlayScreen(),
          EmailScreen(),
          HomeScreen(),
          PagesScreen()
        ],
        physics: NeverScrollableScrollPhysics(),
      ),
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: _currentIndex,
//          onTap: (index)=> _controller.animateToPage(index, duration: Duration(milliseconds: 500), curve: Curves.fastOutSlowIn),
        onTap: (index) {
          _controller.jumpToPage(index);
          setState(() {
            _currentIndex = index;
          });
        },
        type: BottomNavigationBarType.fixed,
        items: [
          BottomNavigationBarItem(
              icon: Icon(
                Icons.home,
                color: _bottomNavigationColor,
              ),
              title: Text(
                'HOME',
                style: TextStyle(color: _bottomNavigationColor),
              )),
          BottomNavigationBarItem(
              icon: Icon(
                Icons.email,
                color: _bottomNavigationColor,
              ),
              title: Text(
                'Email',
                style: TextStyle(color: _bottomNavigationColor),
              )),
          BottomNavigationBarItem(
              icon: Icon(
                Icons.pages,
                color: _bottomNavigationColor,
              ),
              title: Text(
                'PAGES',
                style: TextStyle(color: _bottomNavigationColor),
              )),
          BottomNavigationBarItem(
              icon: Icon(
                Icons.airplay,
                color: _bottomNavigationColor,
              ),
              title: Text(
                'AIRPLAY',
                style: TextStyle(color: _bottomNavigationColor),
              )),
        ],
      ),
    );
  }
}

3.
/**
 * 新增使用pageview实现方式:navigation——keep——alive
 * 这种方式能够通过子页面state实现AutomaticKeepAliveClientMixin
 * 达到切换各页面保持状态的效果。
 * 请切换home注释分别查看
 */
import 'package:flutter/material.dart';
import 'bottom_navigation_widget.dart';

import 'navigation_keep_alive.dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter bottomNavigationBar',
      theme: new ThemeData.dark(),
//      home: BottomNavigationWidget(),
    home: NavigationKeepAlive(),
    );
  }
}

    14.flutter_provide(官方写法不用eventbus实现数据同步刷新共享):https://github.com/geeklx/flutter_app2/tree/master/app2/flutter_provide

1.
import 'package:flutter/material.dart';

class Counter with ChangeNotifier{
  int value = 0;

  increment(){
    value++;
    notifyListeners();
  }
}
2.
import 'package:flutter/material.dart';

class Switcher with ChangeNotifier{
  bool status = false;

  changeStatus(){
    status = !status;
    notifyListeners();
  }
}
3.
import 'package:flutter/material.dart';
import 'package:provide/provide.dart';

import 'models/counter.dart';
import 'models/switcher.dart';
import 'second_screen.dart';

class FirstScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('You have pushed the button this many times:'),
            Provide<Counter>(
              builder: (context, child, counter) {
                return Text(
                  '${counter.value}',
                  style: Theme.of(context).textTheme.display1,
                );
              },
            ),
            Provide<Switcher>(
              builder: (context, child, switcher) {
                return Switch(
                    value: switcher.status,
                    onChanged: (newValue) {
                      switcher.changeStatus();
                    });
              },
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          Navigator.of(context)
              .push(MaterialPageRoute(builder: (context) => SecondScreen()));
        },
        child: Icon(Icons.navigate_next),
      ),
    );
  }
}
4.
import 'package:flutter/material.dart';
import 'package:provide/provide.dart';
import 'models/counter.dart';
import 'models/switcher.dart';

class SecondScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('You have pushed the button this many times:'),
            Provide<Counter>(
              builder: (context, child, counter) {
                return Text(
                  '${counter.value}',
                  style: Theme.of(context).textTheme.display1,
                );
              },
            ),
            Provide<Switcher>(
              builder: (context, child, switcher) {
                return Switch(
                    value: switcher.status,
                    onChanged: (newValue) {
                      switcher.changeStatus();
                    });
              },
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          Provide.value<Counter>(context).increment();
        },
        child: Icon(Icons.add),
      ),
    );
  }
}

    15.flutter_widget_of_the_week(自定义控件部分):https://github.com/geeklx/flutter_app2/tree/master/app2/flutter_widget_of_the_week

    16.frosted_glass_style_demo(毛玻璃效果):https://github.com/geeklx/flutter_app2/tree/master/app2/frosted_glass_style_demo  

blob.png/**
 * 使用BackdropFilter实现毛玻璃效果,且子部件需要设置Opacity
 * 使用这个部件的代价很高,尽量少用
 * ImageFilter.blur的sigmaX/Y决定了毛玻璃的模糊程度,值越高越模糊
 * 一般来说,为了防止模糊效果绘制出边界,需要使用ClipRect Widget包裹
 */
import 'package:flutter/material.dart';
import 'dart:ui';

class FrostedGlassDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      body: new Stack(
        children: <Widget>[
          new ConstrainedBox(
              constraints: const BoxConstraints.expand(),
              child: new FlutterLogo()),
          new Center(
            child: new ClipRect(
              child: new BackdropFilter(
                filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),
                child: Opacity(
                  opacity: 0.5,
                  child: new Container(
                    width: 200.0,
                    height: 200.0,
                  decoration: new BoxDecoration(
//                  color: Colors.grey.shade200.withOpacity(0.5),
                  color: Colors.grey.shade200,
                  ),
                    child: new Center(
                      child: new Text('Frosted',
                          style: Theme.of(context).textTheme.display3),
                    ),
                  ),
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

    17.hero_demo(官方提供的标准跟随动画 目前感觉场景用处不多 可再看看):https://github.com/geeklx/flutter_app2/tree/master/app2/hero_demo

blob.png blob.png

1.
/**
 * 当HeroAnimation作为app的home属性提供时,
 * MaterialApp会隐式推送起始路径。
 * InkWell包装图像,使得向源和目标英雄添加轻击手势变得非常简单。
 * 使用透明颜色定义“材质”窗口小部件可使图像在飞往目标时“弹出”背景。
 * SizedBox指定动画开始和结束时英雄的大小。
 * 将图像的fit属性设置为BoxFit.contain,可确保图像在过渡期间尽可能大,
 * 而不会更改其纵横比。
 */
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart' show timeDilation;

class SourceHeroPage extends StatelessWidget {
  Widget build(BuildContext context) {
    timeDilation = 5.0; // 1.0 means normal animation speed.

    return Scaffold(
      appBar: AppBar(
        title: const Text('Basic Hero Animation'),
      ),
      body: Center(
        child: PhotoHero(
          photo: 'http://pic1.win4000.com/wallpaper/e/537ebd9b60603.jpg',
          width: 300.0,
          onTap: () {
            Navigator.of(context).push(MaterialPageRoute<Null>(
                builder: (BuildContext context) => DestinationHeroPage()));
          },
        ),
      ),
    );
  }
}

class DestinationHeroPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Flippers Page'),
      ),
      body: Container(
        // The blue background emphasizes that it's a new route.
        color: Colors.lightBlueAccent,
        padding: const EdgeInsets.all(16.0),
        alignment: Alignment.topLeft,
        child: PhotoHero(
          photo: 'http://pic1.win4000.com/wallpaper/e/537ebd9b60603.jpg',
          width: 100.0,
          onTap: () {
            Navigator.of(context).pop();
          },
        ),
      ),
    );
  }
}

class PhotoHero extends StatelessWidget {
  const PhotoHero({Key key, this.photo, this.onTap, this.width})
      : super(key: key);

  final String photo;
  final VoidCallback onTap;
  final double width;

  Widget build(BuildContext context) {
    return SizedBox(
      width: width,
      child: Hero(
        tag: photo,
        child: Material(
          color: Colors.transparent,
          child: InkWell(
            onTap: onTap,
            child: Image.network(
              photo,
              fit: BoxFit.contain,
            ),
          ),
        ),
      ),
    );
  }
}

2.
/**
 * 当HeroAnimation作为app的home属性提供时,
 * MaterialApp会隐式推送起始路径。
 * InkWell包装图像,使得向源和目标英雄添加轻击手势变得非常简单。
 * 使用透明颜色定义“材质”窗口小部件可使图像在飞往目标时“弹出”背景。
 * SizedBox指定动画开始和结束时英雄的大小。
 * 将图像的fit属性设置为BoxFit.contain,可确保图像在过渡期间尽可能大,
 * 而不会更改其纵横比。
 */
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart' show timeDilation;

class SourceHeroPage extends StatelessWidget {
  Widget build(BuildContext context) {
    timeDilation = 5.0; // 1.0 means normal animation speed.

    return Scaffold(
      appBar: AppBar(
        title: const Text('Basic Hero Animation'),
      ),
      body: Center(
        child: PhotoHero(
          photo: 'http://pic1.win4000.com/wallpaper/e/537ebd9b60603.jpg',
          width: 300.0,
          onTap: () {
            Navigator.of(context).push(MaterialPageRoute<Null>(
                builder: (BuildContext context) => DestinationHeroPage()));
          },
        ),
      ),
    );
  }
}

class DestinationHeroPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Flippers Page'),
      ),
      body: Container(
        // The blue background emphasizes that it's a new route.
        color: Colors.lightBlueAccent,
        padding: const EdgeInsets.all(16.0),
        alignment: Alignment.topLeft,
        child: PhotoHero(
          photo: 'http://pic1.win4000.com/wallpaper/e/537ebd9b60603.jpg',
          width: 100.0,
          onTap: () {
            Navigator.of(context).pop();
          },
        ),
      ),
    );
  }
}

class PhotoHero extends StatelessWidget {
  const PhotoHero({Key key, this.photo, this.onTap, this.width})
      : super(key: key);

  final String photo;
  final VoidCallback onTap;
  final double width;

  Widget build(BuildContext context) {
    return SizedBox(
      width: width,
      child: Hero(
        tag: photo,
        child: Material(
          color: Colors.transparent,
          child: InkWell(
            onTap: onTap,
            child: Image.network(
              photo,
              fit: BoxFit.contain,
            ),
          ),
        ),
      ),
    );
  }
}

    18.intro_views(这是目前找到的一个不错的标准欢迎页模板,提供大家参考):https://github.com/geeklx/flutter_app2/tree/master/app2/intro_views

blob.png  blob.png

1.
import 'package:flutter/material.dart';
import 'package:intro_views_flutter/Models/page_view_model.dart';
import 'package:intro_views_flutter/intro_views_flutter.dart';

import 'home_page.dart';

class IntroViewDemo extends StatelessWidget {
  //making list of pages needed to pass in IntroViewsFlutter constructor.
  final pages = [
    new PageViewModel(
        pageColor: const Color(0xFF03A9F4),
        iconImageAssetPath: 'assets/air-hostess.png',
        iconColor: null,
        bubbleBackgroundColor: null,
        body: Text(
          'Haselfree  booking  of  flight  tickets  with  full  refund  on  cancelation',
        ),
        title: Text(
          'Flights',
        ),
        textStyle: TextStyle(fontFamily: 'MyFont', color: Colors.white),
        mainImage: Image.asset(
          'assets/airplane.png',
          height: 285.0,
          width: 285.0,
          alignment: Alignment.center,
        )),
    new PageViewModel(
      pageColor: const Color(0xFF8BC34A),
      iconImageAssetPath: 'assets/waiter.png',
      iconColor: null,
      bubbleBackgroundColor: null,
      body: Text(
        'We  work  for  the  comfort ,  enjoy  your  stay  at  our  beautiful  hotels',
      ),
      title: Text('Hotels'),
      mainImage: Image.asset(
        'assets/hotel.png',
        height: 285.0,
        width: 285.0,
        alignment: Alignment.center,
      ),
      textStyle: TextStyle(fontFamily: 'MyFont', color: Colors.white),
    ),
    new PageViewModel(
      pageColor: const Color(0xFF607D8B),
      iconImageAssetPath: 'assets/taxi-driver.png',
      iconColor: null,
      bubbleBackgroundColor: null,
      body: Text(
        'Easy  cab  booking  at  your  doorstep  with  cashless  payment  system',
      ),
      title: Text('Cabs'),
      mainImage: Image.asset(
        'assets/taxi.png',
        height: 285.0,
        width: 285.0,
        alignment: Alignment.center,
      ),
      textStyle: TextStyle(fontFamily: 'MyFont', color: Colors.white),
    ),
  ];

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'IntroViews Flutter', //title of app
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ), //ThemeData
      home: new Builder(
        builder: (context) => new IntroViewsFlutter(
              pages,
              onTapDoneButton: () {
                Navigator.push(
                  context,
                  new MaterialPageRoute(
                    builder: (context) =>
                        new MyHomePage(title: 'Flutter Demo Home Page'),
                  ), //MaterialPageRoute
                );
              },
              showSkipButton:
                  true, //Whether you want to show the skip button or not.
              pageButtonTextStyles: TextStyle(
                color: Colors.white,
                fontSize: 18.0,
              ),
            ), //IntroViewsFlutter
      ), //Builder
    ); //Material App
  }
}

2.
import 'package:flutter/material.dart';

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text(widget.title),
      ),
      body: new Center(
        child: new Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            new Text(
              'You have pushed the button this many times:',
            ),
            new Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),
      floatingActionButton: new FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: new Icon(Icons.add),
      ),
    );
  }
}

    19.keep_alive_demo(类似于fragment信息保存于同一activity的意思):https://github.com/geeklx/flutter_app2/tree/master/app2/keep_alive_demo

blob.png

1.
import 'package:flutter/material.dart';
import 'package:keep_alive_demo/keep_alive_demo.dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: KeepAliveDemo(),
    );
  }
}

class KeepAliveDemo extends StatefulWidget {
  @override
  _KeepAliveDemoState createState() => _KeepAliveDemoState();
}

class _KeepAliveDemoState extends State<KeepAliveDemo>
    with SingleTickerProviderStateMixin {
  TabController _controller;
  final _bottomNavigationColor = Colors.blue;
  int _currentIndex = 0;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    _controller = TabController(length: 3, vsync: this);
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        bottom: TabBar(
          controller: _controller,
          tabs: [
            Tab(icon: Icon(Icons.directions_car)),
            Tab(icon: Icon(Icons.directions_transit)),
            Tab(icon: Icon(Icons.directions_bike)),
          ],
        ),
        title: Text('Keep Alive Demo'),
      ),
      body: TabBarView(
        controller: _controller,
        children: [
          MyHomePage(
            title: 'Keep Alive demo',
          ),
          MyHomePage(
            title: 'Keep Alive demo',
          ),
          MyHomePage(
            title: 'Keep Alive demo',
          ),
        ],
      ),
      bottomNavigationBar: BottomNavigationBar(
        items: [
          BottomNavigationBarItem(
              icon: Icon(
                Icons.home,
                color: _bottomNavigationColor,
              ),
              title: Text(
                'HOME',
                style: TextStyle(color: _bottomNavigationColor),
              )),
          BottomNavigationBarItem(
              icon: Icon(
                Icons.email,
                color: _bottomNavigationColor,
              ),
              title: Text(
                'Email',
                style: TextStyle(color: _bottomNavigationColor),
              )),
          BottomNavigationBarItem(
              icon: Icon(
                Icons.pages,
                color: _bottomNavigationColor,
              ),
              title: Text(
                'PAGES',
                style: TextStyle(color: _bottomNavigationColor),
              )),
          BottomNavigationBarItem(
              icon: Icon(
                Icons.airplay,
                color: _bottomNavigationColor,
              ),
              title: Text(
                'AIRPLAY',
                style: TextStyle(color: _bottomNavigationColor),
              )),
        ],
        currentIndex: _currentIndex,
        onTap: (int index) {
          setState(() {
            _currentIndex = index;
          });
        },
      ),
    );
  }
}

2.
import 'package:flutter/material.dart';
/**
 * 实现原理,使用AutomaticKeepAliveClientMixin,并重写wantKeepAlive方法,让状态不被回收掉。
 */

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with AutomaticKeepAliveClientMixin{
  int _counter = 0;

  // TODO: implement wantKeepAlive
  @override
  bool get wantKeepAlive => true;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      body: new Center(
        child: new Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            new Text(
              'You have pushed the button this many times:',
            ),
            new Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),
      floatingActionButton: new FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: new Icon(Icons.add),
      ),
    );
  }


}

    20.load_multi_image(图片多选上传,目前的问题是列表展示是小图 看不清 正在想解决方案):https://github.com/geeklx/flutter_app2/tree/master/app2/load_multi_image

blob.png blob.png

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:multi_image_picker/asset.dart';
import 'package:multi_image_picker/multi_image_picker.dart';

class LoadImageDemo extends StatefulWidget {
  @override
  _LoadImageDemoState createState() => _LoadImageDemoState();
}

class _LoadImageDemoState extends State<LoadImageDemo> {
  List<Asset> images = List<Asset>();
  String _error;

  Future<void> loadAssets() async {
    setState(() {
      images = List<Asset>();
    });
    List resultList;
    try {
      resultList = await MultiImagePicker.pickImages(
        maxImages: 300,
      );
    } on PlatformException catch (e) {
      _error = e.message;
    }
    // If the widget was removed from the tree while the asynchronous platform
    // message was in flight, we want to discard the reply rather than calling
    // setState to update our non-existent appearance.
    if (!mounted) return;

    setState(() {
      images = resultList;
      if (_error == null) _error = 'No Error Dectected';
    });
  }

  Widget builtImage(Asset asset) {
    if (asset.thumbData != null) {
      return Image.memory(
        asset.thumbData.buffer.asUint8List(),
        fit: BoxFit.cover,
        gaplessPlayback: true,
      );
    }
    return Container();
  }

  void _loadImage(Asset asset) async {
    await asset.requestThumbnail(300, 300);
//    await asset.releaseThumb();
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('LoadImageDemo'),
      ),
      body: ListView.builder(
          itemCount: images.length,
          itemBuilder: (context, index) {
            _loadImage(images[index]);
            return builtImage(images[index]);
          }),
      floatingActionButton: FloatingActionButton(
        onPressed: loadAssets,
        child: Icon(Icons.image),
      ),
    );
  }
}

    21.overlay(overlay组件能够让我们在当前页面上层覆盖一层新的组件):https://github.com/geeklx/flutter_app2/tree/master/app2/overlay

/**
 * 请切换import中的注释分别查看
 */
import 'package:flutter/material.dart';
//import 'overlay_demo.dart';
//import 'overlay_demo2.dart';
import 'overlay_demo3.dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: OverlayDemo(),
    );
  }
}

    22.pinch_zoom_image_demo(很不错的第三方图片加载有缓存机制):https://github.com/geeklx/flutter_app2/tree/master/app2/pinch_zoom_image_demo

blob.png

import 'package:flutter/material.dart';
import 'package:pinch_zoom_image/pinch_zoom_image.dart';
import 'package:cached_network_image/cached_network_image.dart';

class PinchZoomImageDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Pinch Zoom Image Demo'),
      ),
      body: ListView(
        children: <Widget>[
          PinchZoomImage(
            image: Image.network('https://s2.51cto.com/wyfs02/M01/89/BA/wKioL1ga-u7QnnVnAAAfrCiGnBQ946_middle.jpg'),
            zoomedBackgroundColor: Color.fromRGBO(240, 240, 240, 1.0),
          ),
          PinchZoomImage(
            image: Image(
              image:
                  CachedNetworkImageProvider(
                      'https://s2.51cto.com/wyfs02/M01/89/BA/wKioL1ga-u7QnnVnAAAfrCiGnBQ946_middle.jpg',
                  ),
            ),
            zoomedBackgroundColor: Color.fromRGBO(240, 240, 240, 1.0),
          ),
        ],
      ),
    );
  }
}

    23.pull_on_loading(下拉刷新 很标准的写法):https://github.com/geeklx/flutter_app2/tree/master/app2/pull_on_loading

blob.png

    24.redux_demo(页面传值标准框架用法举例demo 很不错):https://github.com/geeklx/flutter_app2/tree/master/app2/redux_demo

blob.png blob.png

1.
import 'package:meta/meta.dart';

/**
 * State中所有属性都应该是只读的
 */
@immutable
class CountState {
  final int _count;
  get count => _count;

  CountState(this._count);

  CountState.initState() : _count = 0;
}

/**
 * 定义操作该State的全部Action
 * 这里只有增加count一个动作
 */
enum Action { increment }

/**
 * reducer会根据传进来的action生成新的CountState
 */
CountState reducer(CountState state, action) {
  //匹配Action
  if (action == Action.increment) {
    return CountState(state.count + 1);
  }
  return state;
}

2.
import 'package:flutter/material.dart';
import 'package:redux_demo/under_screen.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:redux_demo/states/count_state.dart';

class TopScreen extends StatefulWidget {
  @override
  _TopScreenState createState() => _TopScreenState();
}

class _TopScreenState extends State<TopScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Top Screen'),
      ),
      body: Center(
        child: StoreConnector<CountState,int>(
          converter: (store) => store.state.count,
          builder: (context, count) {
            return Text(
              count.toString(),
              style: Theme.of(context).textTheme.display1,
            );
          },
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context){
            return UnderScreen();
          }));
        },
        child: Icon(Icons.forward),
      ),
    );
  }
}

3.
import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:redux_demo/states/count_state.dart';

class UnderScreen extends StatefulWidget {
  @override
  _UnderScreenState createState() => _UnderScreenState();
}

class _UnderScreenState extends State<UnderScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Under Screen'),
      ),
      body: Center(
        child: new Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            new Text(
              'You have pushed the button this many times:',
            ),
            StoreConnector<CountState,int>(
              converter: (store) => store.state.count,
              builder: (context, count) {
                return Text(
                  count.toString(),
                  style: Theme.of(context).textTheme.display1,
                );
              },
            ),
          ],
        ),
      ),
      floatingActionButton: StoreConnector<CountState,VoidCallback>(

        converter: (store) {
          return () => store.dispatch(Action.increment);
        },
        builder: (context, callback) {
          return FloatingActionButton(
            onPressed: callback,
            child: Icon(Icons.add),
          );
        },
      ),
    );
  }
}

    未完待续...