Flutter第八期-控件总结篇

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

成都创新互联是工信部颁发资质IDC服务器商,为用户提供优质的成都多线服务器托管服务

    附:效果图因为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,好记性不如个烂笔头。

Flutter第八期 - 控件总结篇 Flutter第八期 - 控件总结篇

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 {
  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):

Flutter第八期 - 控件总结篇 Flutter第八期 - 控件总结篇

import 'package:flutter/material.dart';

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

class _AnimatedCrossFadeDemoState extends State {
  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

 Flutter第八期 - 控件总结篇

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 {
  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: [
            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):

Flutter第八期 - 控件总结篇

    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 {
  List 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 {
  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: [
          ListView(
            shrinkWrap: true,
            padding: const EdgeInsets.symmetric(horizontal: 20.0),
            children: [
              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
    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: [
              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 {
  Stopwatch stopwatch = Stopwatch();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        color: Colors.orangeAccent.withOpacity(0.2),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            _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: [
      _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 {
  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 formattedTime =
        TimerTextFormatter.format(stopwatch.elapsedMilliseconds);
    return Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        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 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 {
  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: [
            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: [
            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: [
          SizedBox(
            width: MediaQuery.of(context).size.width / 4,
          ),
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.end,
              children: [
                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: [
       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
   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):由于篇幅有限代码就不说了,地址附上 Flutter第八期 - 控件总结篇 Flutter第八期 - 控件总结篇

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

Flutter第八期 - 控件总结篇

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

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

class _SearchBarDemoState extends State {
 @override
 Widget build(BuildContext context) {
   return Scaffold(
     appBar: AppBar(
       title: Text('SearchBarDemo'),
       actions: [
         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 {
 @override
 List 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

Flutter第八期 - 控件总结篇

import 'dart:async';

import './bloc_base.dart';

class IncrementBloc implements BlocBase {
 int _counter;

 StreamController _counterPipe = StreamController();
 Stream 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 extends StatefulWidget {
 BlocProvider({
   Key key,
   @required this.child,
   @required this.bloc,
 }) : super(key: key);

 final T bloc;
 final Widget child;

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

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

 static Type _typeOf() => T;
}

class _BlocProviderState extends State> {
 @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

Flutter第八期 - 控件总结篇

import 'package:flutter/material.dart';

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

class _EachViewState extends State {
 @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 {
 List _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: [
           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

Flutter第八期 - 控件总结篇

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 animation,
             Animation secondaryAnimation) {
           return widget;
         },
         transitionsBuilder: (BuildContext context,
             Animation animation,
             Animation 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(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

Flutter第八期 - 控件总结篇

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: [
              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 {
  var currentPanelIndex = -1;

  List mList;
  //用来保存expansionPanel的状态
  List 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

Flutter第八期 - 控件总结篇

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

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

class _AirPlayScreenState extends State
    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: [
            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
    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: [
          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
本文名称:Flutter第八期-控件总结篇
本文网址:http://pwwzsj.com/article/gdechc.html