休息片刻后,我将继续以问答形式讨论流行的Flutter框架。 您可以在
此处找到针对Android开发人员的第一篇文章,而今天,将为iOS开发人员提供有用的材料。
如果您没有时间来独立和深入地研究文档,但是您想了解Flutter的优点和用法,请仔细阅读。
扑 第1部分。对于Android开发人员扑 第2部分。对于iOS开发人员扑 第3部分。对于React本机开发人员扑 第4部分。对于Web开发人员扑 第5部分。对于Xamarin.Forms开发人员
内容:
- 观看次数
- 导览
- 线程与异步
- 项目结构与资源
- ViewControllers
- 版面
- 手势和触摸事件处理
- 应用程式样式
- 输入形式
- Flutter插件
- 数据库和本地存储
- 通知事项
观看次数
问题:
Flutter中的
UIView模拟是什么?
答案是:
小部件差异:
UIView实际上是屏幕上显示的内容。 调用SetNeedsDisplay()来显示更改。
小部件 -屏幕上显示的内容。 为了改变而重新创建。
附加信息:
Flutter包括
Cupertino Widgets库。 它包含实现
Apple设计准则的小部件。
问题:
如何更新小部件的显示?
答案是:
使用
StatefulWidget及其
状态 。 Flutter有2种小部件:
StatelessWidget和
StatefulWidget 。 它们以相同的方式工作,唯一的区别在于渲染状态。
差异:
StatelessWidget具有不可变状态。 适用于显示文本,徽标等。 即 如果屏幕上的元素在整个显示时间内均不应更改,则适合您。 它也可以用作有状态窗口小部件的容器。
StatefulWidget具有状态状态,该状态存储有关当前状态的信息。 如果要在执行某些操作时更改屏幕上的元素(来自服务器的响应,用户单击按钮等),这是您的选择。
一个例子:
1)StatelessWidget-文字
Text( 'I like Flutter!', style: TextStyle(fontWeight: FontWeight.bold), );
2)StatefulWidget-当您单击(FloatingActionButton)按钮时,“文本”小部件中的文本从“
我喜欢颤动”变为“
颤动很棒”! import 'package:flutter/material.dart'; void main() { runApp(SampleApp()); } class SampleApp extends StatelessWidget {
问题:
如何用小部件布置屏幕?
故事板在哪里?
答案是:
Flutter没有
情节提要 。 一切都直接在代码中在小部件树中排版。
一个例子:
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Sample App"), ), body: Center( child: CupertinoButton( onPressed: () { setState(() { _pressedCount += 1; }); }, child: Text('Hello'), padding: EdgeInsets.only(left: 10.0, right: 10.0), ), ), ); }
Flutter中的所有默认小部件都可以在
小部件目录中查看 。
问题:
在应用程序运行时如何在布局中添加或删除组件?
答案是:
通过一个函数,该函数将根据状态返回所需的小部件。
差异:
在iOS上,您可以执行addSubview()或removeFromSuperview()。 在Flutter中,这是不可能的,因为 小部件不变。 只有他们的状况可以改变。
一个例子:
通过单击FloatingActionButton将文本更改为Button。
class SampleApp extends StatelessWidget {
问题:
如何为小部件制作动画?
答案是:
使用
AnimationController类,它是
Animation <T>抽象类的后代。 除了开始播放动画外,他还可以暂停,倒带,停止并以相反的方向播放。 与
Ticker一起使用 ,它报告屏幕重绘。
差异:
在iOS上,您可以使用animate(withDuration:animations :)对视图进行动画处理。 在Flutter中,必须使用AnimationController用代码编写动画。
附加信息:
您可以在“
动画和运动”小部件 ,“
动画”教程和“
动画”概述中了解更多信息 。
一个例子:
Flutter徽标的淡入淡出动画。
class SampleApp extends StatelessWidget {
问题:
如何使用
CoreGraphics ?
答案是:
Flutter在
Skia低级引擎上使用了Canvas API,而不是CoreGraphics。 Android使用类似的Canvas API。
附加信息:
Flutter有两个用于在Canvas上绘制的类:
CustomPaint和
CustomPainter 。 第二个实现您的渲染算法。
在此处阅读更多信息:
StackOverflow一个例子:
class SignaturePainter extends CustomPainter { SignaturePainter(this.points); final List<Offset> points; void paint(Canvas canvas, Size size) { var paint = Paint() ..color = Colors.black ..strokeCap = StrokeCap.round ..strokeWidth = 5.0; for (int i = 0; i < points.length - 1; i++) { if (points[i] != null && points[i + 1] != null) canvas.drawLine(points[i], points[i + 1], paint); } } bool shouldRepaint(SignaturePainter other) => other.points != points; } class Signature extends StatefulWidget { SignatureState createState() => SignatureState(); } class SignatureState extends State<Signature> { List<Offset> _points = <Offset>[]; Widget build(BuildContext context) { return GestureDetector( onPanUpdate: (DragUpdateDetails details) { setState(() { RenderBox referenceBox = context.findRenderObject(); Offset localPosition = referenceBox.globalToLocal(details.globalPosition); _points = List.from(_points)..add(localPosition); }); }, onPanEnd: (DragEndDetails details) => _points.add(null), child: CustomPaint(painter: SignaturePainter(_points), size: Size.infinite), ); } }
问题:
如何更改小部件的透明度?
答案是:
包装
不透明度小部件。
差异:
在iOS上,所有视图均具有.opacity或.alpha。 在Flutter中,此选项将替换包装窗口小部件。
问题:
如何创建自定义窗口小部件?
答案是:
在一个组件中组成小部件(而不是继承)。
差异:
在iOS中,您可以从我们感兴趣的视图继承并添加自己的逻辑。 在Flutter中,小部件始终从StatelessWidget或StatefulWidget继承。 即 您需要创建一个新的小部件,并将其用作参数或字段所需的一组小部件。
一个例子:
class CustomButton extends StatelessWidget { final String label; CustomButton(this.label); @override Widget build(BuildContext context) { return RaisedButton(onPressed: () {}, child: Text(label)); } } @override Widget build(BuildContext context) { return Center( child: CustomButton("Hello"), ); }
导览
问题:
如何在Flutter中实现屏幕之间的导航?
答案是:
要在屏幕之间导航,请使用
Navigator和
Route类。
差异:
Flutter没有UIViewController和UINavigationController这样的概念。 有导航器(Navigator)和路线(routes)。 导航器原则上类似于UINavigationController。 它可以
将()或
pop() 推送到您指定的路线。 Route是一种UIViewController,但在Flutter中,习惯将其与屏幕或页面进行比较。
Flutter有两种导航方法:
一个例子:
void main() { runApp(CupertinoApp( home: MyAppHome(),
问题:
如何导航到第三方应用程序?
答案是:
通过
MethodChannel或使用
URL启动器插件与应用程序的iOS层进行交互。
问题:
如何在iOS ViewController中弹出弹出窗口?
答案是:
通过调用SystemNavigator.pop()。
附加信息:
Dart代码中的SystemNavigator.pop()在iOS中调用以下代码:
UIViewController* viewController = [UIApplication sharedApplication].keyWindow.rootViewController; if ([viewController isKindOfClass:[UINavigationController class]]) { [((UINavigationController*)viewController) popViewControllerAnimated:NO]; }
如果这不是您所需要的,则可以通过
MethodChannel进行实现。
线程与异步
问题:
如何在Flutter中编写异步代码?
答案是:
Dart实现了一个在
Isolates上运行的单线程执行模型。 异步执行使用async / await,您可能对C#,JavaScript或Kotlin协程很熟悉。
一个例子:
完成请求并返回结果以更新UI:
loadData() async { String dataURL = "https://jsonplaceholder.typicode.com/posts"; http.Response response = await http.get(dataURL); setState(() { widgets = json.decode(response.body); }); }
收到对请求的响应后,您需要调用
setState()方法以使用新数据重绘窗口小部件树。
一个例子:
在
ListView中加载和更新数据:
import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; void main() { runApp(SampleApp()); } class SampleApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Sample App', theme: ThemeData( primarySwatch: Colors.blue, ), home: SampleAppPage(), ); } } class SampleAppPage extends StatefulWidget { SampleAppPage({Key key}) : super(key: key); @override _SampleAppPageState createState() => _SampleAppPageState(); } class _SampleAppPageState extends State<SampleAppPage> { List widgets = []; @override void initState() { super.initState(); loadData(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Sample App"), ), body: ListView.builder( itemCount: widgets.length, itemBuilder: (BuildContext context, int position) { return getRow(position); })); } Widget getRow(int i) { return Padding( padding: EdgeInsets.all(10.0), child: Text("Row ${widgets[i]["title"]}") ); } loadData() async { String dataURL = "https://jsonplaceholder.typicode.com/posts"; http.Response response = await http.get(dataURL); setState(() { widgets = json.decode(response.body); }); } }
问题:
如何在后台线程中执行代码?
答案是:
如上所述-使用异步/等待和隔离(Isolate)。
差异:
在iOS上,您可以直接使用Operation并重新定义方法。 在Flutter“开箱即用”中,您只需要使用async / await,Dart将负责其余的工作。
一个例子:
这里的dataLoader()方法是隔离的。 孤立地,您可以运行繁重的操作,例如解析大型JSON,加密,图像处理等。
loadData() async { ReceivePort receivePort = ReceivePort(); await Isolate.spawn(dataLoader, receivePort.sendPort);
问题:
如何在Flutter中发出网络请求?
答案是:
Flutter有自己的
HTTP包 。
一个例子:
要使用HTTP包,请将其添加为pubspec.yaml中的依赖项:
dependencies: ... http: ^0.11.3+16
要执行请求,请在异步函数http.get()中调用await:
import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; [...] loadData() async { String dataURL = "https://jsonplaceholder.typicode.com/posts"; http.Response response = await http.get(dataURL); setState(() { widgets = json.decode(response.body); }); } }
问题:
如何显示进度?
答案是:
使用
ProgressIndicator小部件。
一个例子:
import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; void main() { runApp(SampleApp()); } class SampleApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Sample App', theme: ThemeData( primarySwatch: Colors.blue, ), home: SampleAppPage(), ); } } class SampleAppPage extends StatefulWidget { SampleAppPage({Key key}) : super(key: key); @override _SampleAppPageState createState() => _SampleAppPageState(); } class _SampleAppPageState extends State<SampleAppPage> { List widgets = []; @override void initState() { super.initState(); loadData(); } showLoadingDialog() { return widgets.length == 0; } getBody() { if (showLoadingDialog()) { return getProgressDialog(); } else { return getListView(); } } getProgressDialog() { return Center(child: CircularProgressIndicator()); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Sample App"), ), body: getBody()); } ListView getListView() => ListView.builder( itemCount: widgets.length, itemBuilder: (BuildContext context, int position) { return getRow(position); }); Widget getRow(int i) { return Padding(padding: EdgeInsets.all(10.0), child: Text("Row ${widgets[i]["title"]}")); } loadData() async { String dataURL = "https://jsonplaceholder.typicode.com/posts"; http.Response response = await http.get(dataURL); setState(() { widgets = json.decode(response.body); }); } }
项目结构与资源
问题:
哪里存储不同分辨率的资源?
答案是:
在资产中。
差异:
在iOS中,图形资源具有Images.xcasset,位于资源文件夹中。 Flutter只有资产。 资源文件夹可以位于项目中的任何位置,最重要的是,在pubspec.yaml文件中写入它的路径。
附加信息:
iOS和Flutter中的图形资源大小相同,并且遵循基于密度的格式。
资源位置:
images/my_icon.png // Base: 1.0x image images/2.0x/my_icon.png // 2.0x image images/3.0x/my_icon.png // 3.0x image
pubspec.yaml文件中的路径:
assets: - images/my_icon.png
使用
AssetImage :
return AssetImage("images/a_dot_burr.jpeg");
直接使用资产:
@override Widget build(BuildContext context) { return Image.asset("images/my_image.png"); }
问题:
在哪里存储字符串? 如何定位它们?
答案是:
存储在静态字段中。 使用
intl包进行本地化。
一个例子:
class Strings { static String welcomeMessage = "Welcome To Flutter"; } Text(Strings.welcomeMessage)
问题:
CocoaPods的对应物是什么? 如何添加依赖项?
答案是:
pubspec.yaml。
附加信息:
Flutter将构建委托给本地Android和iOS构建器。 在
Pub上查看Flutter的所有流行图书馆的列表。
ViewControllers
问题:
什么是Flutter中的
ViewController ?
答案是:
Flutter中的所有内容都是小部件。 小部件扮演着ViewController处理UI的角色。 正如导航部分中提到的,导航的角色是“导航器”和“路线”。
问题:
如何处理生命周期事件?
答案是:
使用
WidgetsBinding和
didChangeAppLifecycleState()方法。
附加信息:
Flutter在本机代码中使用FlutterAppDelegate,而Flutter引擎使处理状态更改尽可能不明显。 但是,如果您仍然需要根据状态进行一些工作,则生命周期会略有不同:
- 不活动-应用程序不活动,不接收用户输入。 此状态仅在iOS中存在,在Android中没有类似状态。
- 已暂停-该应用程序当前对用户不可见,不响应用户输入,但在后台运行;
- 恢复-应用程序可见并且响应用户输入;
- 暂停-正在停止的应用程序。 此状态仅在Android中,在iOS中没有类似物。
这在
AppLifecycleStatus文档中有更详细的描述。
一个例子:
import 'package:flutter/widgets.dart'; class LifecycleWatcher extends StatefulWidget { @override _LifecycleWatcherState createState() => _LifecycleWatcherState(); } class _LifecycleWatcherState extends State<LifecycleWatcher> with WidgetsBindingObserver { AppLifecycleState _lastLifecycleState; @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); } @override void dispose() { WidgetsBinding.instance.removeObserver(this); super.dispose(); } @override void didChangeAppLifecycleState(AppLifecycleState state) { setState(() { _lastLifecycleState = state; }); } @override Widget build(BuildContext context) { if (_lastLifecycleState == null) return Text('This widget has not observed any lifecycle changes.', textDirection: TextDirection.ltr); return Text('The most recent lifecycle state this widget observed was: $_lastLifecycleState.', textDirection: TextDirection.ltr); } } void main() { runApp(Center(child: LifecycleWatcher())); }
版面
问题:
UITableView和
UICollectionView等效于什么?
答案是:
列表视图一个例子:
import 'package:flutter/material.dart'; void main() { runApp(SampleApp()); } class SampleApp extends StatelessWidget {
问题:
我如何知道单击了哪个列表项?
答案是:
作为列表元素的小部件必须处理对其的单击。
差异:
在iOS中,单独的tableView:didSelectRowAtIndexPath:方法对此负责。 在Flutter中,列表项必须包装在处理点击的小部件中,例如
GestureDetector 。
问题:
如何动态更新
ListView ?
答案是:
刷新数据列表并调用setState()。
差异:
在iOS上,您需要更新数据并调用reloadData方法。 在Flutter中,在setState()之后,小部件将再次重绘。
一个例子:
import 'package:flutter/material.dart'; void main() { runApp(SampleApp()); } class SampleApp extends StatelessWidget {
附加信息:
要创建列表,建议使用
ListView.Builder 。
一个例子:
import 'package:flutter/material.dart'; void main() { runApp(SampleApp()); } class SampleApp extends StatelessWidget {
问题:
什么是
UIScrollView的类似物?
答案是:
具有窗口小部件的
ListView 。
一个例子:
@override Widget build(BuildContext context) { return ListView( children: <Widget>[ Text('Row One'), Text('Row Two'), Text('Row Three'), Text('Row Four'), ], ); }
附加信息:
更多细节
在这里 。
手势和触摸事件处理
问题:
如何在Flutter中为小部件添加onClick侦听器?
答案是:
如果小部件支持单击,则在onPressed()中。 如果不是,则在onTap()中。
一个例子:
在onPressed()中:
@override Widget build(BuildContext context) { return RaisedButton( onPressed: () { print("click"); }, child: Text("Button"), ); }
在onTap()中:
class SampleApp extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( body: Center( child: GestureDetector( child: FlutterLogo( size: 200.0, ), onTap: () { print("tap"); }, ), ), ); } }
问题:
如何处理小部件上的其他手势?
答案是:
使用
GestureDetector 。 他们可以执行以下操作:
点按
双击
长按
垂直拖曳
水平拖曳
一个例子:
在DoubleTap上处理:
AnimationController controller; CurvedAnimation curve; @override void initState() { controller = AnimationController(duration: const Duration(milliseconds: 2000), vsync: this); curve = CurvedAnimation(parent: controller, curve: Curves.easeIn); } class SampleApp extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( body: Center( child: GestureDetector( child: RotationTransition( turns: curve, child: FlutterLogo( size: 200.0, )), onDoubleTap: () { if (controller.isCompleted) { controller.reverse(); } else { controller.forward(); } }, ), ), ); } }
应用程式样式
问题:
如何在应用程序中使用主题(Theme)?
答案是:
使用MaterialApp小部件或WidgetApp作为应用程序中的根。
一个例子:
class SampleApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Sample App', theme: ThemeData( primarySwatch: Colors.blue, textSelectionColor: Colors.red ), home: SampleAppPage(), ); } }
问题:
如何使用自定义字体?
答案是:
您只需要将字体文件放在文件夹中(请自己考虑一下名称),然后在pubspec.yaml中指定其路径。
一个例子:
fonts: - family: MyCustomFont fonts: - asset: fonts/MyCustomFont.ttf - style: italic
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Sample App"), ), body: Center( child: Text( 'This is a custom font text', style: TextStyle(fontFamily: 'MyCustomFont'), ), ), ); }
问题:
如何设置文字小部件的样式?
答案是:
使用参数:
- 颜色
- 装饰
- 装饰颜色;
- 装饰风格;
- fontFamily;
- fontSize;
- fontStyle;
- fontWeight;
- hashCode;
- 高度
- 继承
- letterSpacing;
- textBaseline;
- wordSpacing。
输入形式
问题:
如何获得用户输入结果?
答案是:
使用
TextEditingController 。
一个例子:
class _MyFormState extends State<MyForm> {
:
Retrieve the value of a text field .
:
hint
TextInput ?
答案是:
InputDecoration , .
一个例子:
body: Center( child: TextField( decoration: InputDecoration(hintText: "This is a hint"), ), )
:
?
答案是:
—
InputDecoration .
一个例子:
class SampleApp extends StatelessWidget {
Flutter
:
GPS?
答案是:
geolocator .
:
?
答案是:
image_picker .
:
Facebook?
答案是:
flutter_facebook_login .
:
Firebase?
答案是:
Firebase
Flutter first party plugins :
:
() ?
答案是:
Flutter EventBus . :
developing packages and plugins .
:
UserDefault?
答案是:
Shared_Preferences plugin ( Shared Preferences Android ).
一个例子:
import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; void main() { runApp( MaterialApp( home: Scaffold( body: Center( child: RaisedButton( onPressed: _incrementCounter, child: Text('Increment Counter'), ), ), ), ), ); } _incrementCounter() async { SharedPreferences prefs = await SharedPreferences.getInstance(); int counter = (prefs.getInt('counter') ?? 0) + 1; print('Pressed $counter times.'); prefs.setInt('counter', counter); }
:
Core Data ?
答案是:
SQFlite .
:
push-?
答案是:
Firebase_Messaging .
结论
. , , . « » . «-» . , ? 2016 Kotlin, - 2017. , , . , .
2016 Flutter Dart. , 2018 . . ! , , , . ( Google Fuchsia , , , Flutter ). — ! , — . . Apple Store!