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,好记性不如个烂笔头。
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):
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
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):
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{
Listlist;
@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,
);
ListformattedTime =
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 Listformat(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):由于篇幅有限代码就不说了,地址附上
6.beaytiful_search_bar_demo(搜索栏):https://github.com/geeklx/flutter_app2/tree/master/app2/beaytiful_search_bar_demo
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
ListbuildActions(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
import 'dart:async';
import './bloc_base.dart';
class IncrementBloc implements BlocBase {
int _counter;
StreamController_counterPipe = StreamController ();
Streamget 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 BlocProviderextends StatefulWidget {
BlocProvider({
Key key,
@required this.child,
@required this.bloc,
}) : super(key: key);
final T bloc;
final Widget child;
@override
_BlocProviderStatecreateState() => _BlocProviderState ();
static T of(BuildContext context) {
final type = _typeOf>();
BlocProviderprovider = context.ancestorWidgetOfExactType(type);
return provider.bloc;
}
static Type _typeOf() => T;
}
class _BlocProviderStateextends 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
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
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, Animationanimation,
AnimationsecondaryAnimation) {
return widget;
},
transitionsBuilder: (BuildContext context,
Animationanimation,
AnimationsecondaryAnimation,
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
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;
ListmList;
//用来保存expansionPanel的状态
ListexpandStateList;
_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
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