您好,登录后才能下订单哦!
最近写前端,没有总结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,好记性不如个烂笔头。
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):
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
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):
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):由于篇幅有限代码就不说了,地址附上
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<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
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
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
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
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
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
/**
* 使用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
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
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
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
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
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
24.redux_demo(页面传值标准框架用法举例demo 很不错):https://github.com/geeklx/flutter_app2/tree/master/app2/redux_demo
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),
);
},
),
);
}
}
未完待续...
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。