[在码头旁]颤抖。 第2部分。对于iOS开发人员

休息片刻后,我将继续以问答形式讨论流行的Flutter框架。 您可以在此处找到针对Android开发人员的第一篇文章,而今天,将为iOS开发人员提供有用的材料。

如果您没有时间来独立和深入地研究文档,但是您想了解Flutter的优点和用法,请仔细阅读。



第1部分。对于Android开发人员
第2部分。对于iOS开发人员
第3部分。对于React本机开发人员
第4部分。对于Web开发人员
扑 第5部分。对于Xamarin.Forms开发人员

内容:


  1. 观看次数

  2. 导览

  3. 线程与异步

  4. 项目结构与资源

  5. ViewControllers

  6. 版面

  7. 手势和触摸事件处理

  8. 应用程式样式

  9. 输入形式

  10. Flutter插件

  11. 数据库和本地存储

  12. 通知事项



观看次数


问题:


Flutter中的UIView模拟是什么?

答案是:


小部件

差异:


UIView实际上是屏幕上显示的内容。 调用SetNeedsDisplay()来显示更改。

小部件 -屏幕上显示的内容。 为了改变而重新创建。

附加信息:


Flutter包括Cupertino Widgets库。 它包含实现Apple设计准则的小部件。

问题:


如何更新小部件的显示?

答案是:


使用StatefulWidget及其状态 。 Flutter有2种小部件: StatelessWidgetStatefulWidget 。 它们以相同的方式工作,唯一的区别在于渲染状态。

差异:


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 { //     . @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> { //   String textToShow = "  Flutter"; void _updateText() { setState(() { //   textToShow = "Flutter !"; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Sample App"), ), body: Center(child: Text(textToShow)), floatingActionButton: FloatingActionButton( onPressed: _updateText, tooltip: ' ', child: Icon(Icons.update), ), ); } } 

问题:


如何用小部件布置屏幕? 故事板在哪里?

答案是:


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 { // This widget is the root of your application. @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> { // Default value for toggle bool toggle = true; void _toggle() { setState(() { toggle = !toggle; }); } _getToggleChild() { if (toggle) { return Text('Toggle One'); } else { return CupertinoButton( onPressed: () {}, child: Text('Toggle Two'), ); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Sample App"), ), body: Center( child: _getToggleChild(), ), floatingActionButton: FloatingActionButton( onPressed: _toggle, tooltip: 'Update Text', child: Icon(Icons.update), ), ); } } 

问题:


如何为小部件制作动画?

答案是:


使用AnimationController类,它是Animation <T>抽象类的后代。 除了开始播放动画外,他还可以暂停,倒带,停止并以相反的方向播放。 与Ticker一起使用 ,它报告屏幕重绘。

差异:


在iOS上,您可以使用animate(withDuration:animations :)对视图进行动画处理。 在Flutter中,必须使用AnimationController用代码编写动画。

附加信息:


您可以在“ 动画和运动”小部件 ,“ 动画”教程和“ 动画”概述中了解更多信息

一个例子:


Flutter徽标的淡入淡出动画。

 class SampleApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Fade Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: MyFadeTest(title: 'Fade Demo'), ); } } class MyFadeTest extends StatefulWidget { MyFadeTest({Key key, this.title}) : super(key: key); final String title; @override _MyFadeTest createState() => _MyFadeTest(); } class _MyFadeTest extends State<MyFadeTest> with TickerProviderStateMixin { AnimationController controller; CurvedAnimation curve; @override void initState() { controller = AnimationController(duration: const Duration(milliseconds: 2000), vsync: this); curve = CurvedAnimation(parent: controller, curve: Curves.easeIn); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Center( child: Container( child: FadeTransition( opacity: curve, child: FlutterLogo( size: 100.0, ) ) ) ), floatingActionButton: FloatingActionButton( tooltip: 'Fade', child: Icon(Icons.brush), onPressed: () { controller.forward(); }, ), ); } @override dispose() { controller.dispose(); super.dispose(); } } 

问题:


如何使用CoreGraphics

答案是:


Flutter在Skia低级引擎上使用了Canvas API,而不是CoreGraphics。 Android使用类似的Canvas API。

附加信息:


Flutter有两个用于在Canvas上绘制的类: CustomPaintCustomPainter 。 第二个实现您的渲染算法。

在此处阅读更多信息: 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中实现屏幕之间的导航?

答案是:


要在屏幕之间导航,请使用NavigatorRoute类。

差异:


Flutter没有UIViewController和UINavigationController这样的概念。 有导航器(Navigator)和路线(routes)。 导航器原则上类似于UINavigationController。 它可以将()pop() 送到您指定的路线。 Route是一种UIViewController,但在Flutter中,习惯将其与屏幕或页面进行比较。

Flutter有两种导航方法:

  • 用路线名称描述地图
  • 直接导航至路线。

一个例子:


 void main() { runApp(CupertinoApp( home: MyAppHome(), // becomes the route named '/' routes: <String, WidgetBuilder> { '/a': (BuildContext context) => MyPage(title: 'page A'), '/b': (BuildContext context) => MyPage(title: 'page B'), '/c': (BuildContext context) => MyPage(title: 'page C'), }, )); } Navigator.of(context).pushNamed('/b'); 

问题:


如何导航到第三方应用程序?

答案是:


通过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); // The 'echo' isolate sends its SendPort as the first message SendPort sendPort = await receivePort.first; List msg = await sendReceive(sendPort, "https://jsonplaceholder.typicode.com/posts"); setState(() { widgets = msg; }); } // The entry point for the isolate static dataLoader(SendPort sendPort) async { // Open the ReceivePort for incoming messages. ReceivePort port = ReceivePort(); // Notify any other isolates what port this isolate listens to. sendPort.send(port.sendPort); await for (var msg in port) { String data = msg[0]; SendPort replyTo = msg[1]; String dataURL = data; http.Response response = await http.get(dataURL); // Lots of JSON to parse replyTo.send(json.decode(response.body)); } } Future sendReceive(SendPort port, msg) { ReceivePort response = ReceivePort(); port.send([msg, response.sendPort]); return response.first; }   : import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'dart:async'; import 'dart:isolate'; 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() { if (widgets.length == 0) { return true; } return false; } 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 { ReceivePort receivePort = ReceivePort(); await Isolate.spawn(dataLoader, receivePort.sendPort); // The 'echo' isolate sends its SendPort as the first message SendPort sendPort = await receivePort.first; List msg = await sendReceive(sendPort, "https://jsonplaceholder.typicode.com/posts"); setState(() { widgets = msg; }); } // the entry point for the isolate static dataLoader(SendPort sendPort) async { // Open the ReceivePort for incoming messages. ReceivePort port = ReceivePort(); // Notify any other isolates what port this isolate listens to. sendPort.send(port.sendPort); await for (var msg in port) { String data = msg[0]; SendPort replyTo = msg[1]; String dataURL = data; http.Response response = await http.get(dataURL); // Lots of JSON to parse replyTo.send(json.decode(response.body)); } } Future sendReceive(SendPort port, msg) { ReceivePort response = ReceivePort(); port.send([msg, response.sendPort]); return response.first; } } 

问题:


如何在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的角色。 正如导航部分中提到的,导航的角色是“导航器”和“路线”。

问题:


如何处理生命周期事件?

答案是:


使用WidgetsBindingdidChangeAppLifecycleState()方法。

附加信息:


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())); } 

版面


问题:


UITableViewUICollectionView等效于什么?

答案是:


列表视图

一个例子:


 import 'package:flutter/material.dart'; void main() { runApp(SampleApp()); } class SampleApp extends StatelessWidget { // This widget is the root of your application. @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> { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Sample App"), ), body: ListView(children: _getListData()), ); } _getListData() { List<Widget> widgets = []; for (int i = 0; i < 100; i++) { widgets.add(Padding(padding: EdgeInsets.all(10.0), child: Text("Row $i"))); } return widgets; } } 

问题:


我如何知道单击了哪个列表项?

答案是:


作为列表元素的小部件必须处理对其的单击。

差异:


在iOS中,单独的tableView:didSelectRowAtIndexPath:方法对此负责。 在Flutter中,列表项必须包装在处理点击的小部件中,例如GestureDetector

问题:


如何动态更新ListView

答案是:


刷新数据列表并调用setState()。

差异:


在iOS上,您需要更新数据并调用reloadData方法。 在Flutter中,在setState()之后,小部件将再次重绘。

一个例子:


 import 'package:flutter/material.dart'; void main() { runApp(SampleApp()); } class SampleApp extends StatelessWidget { // This widget is the root of your application. @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(); for (int i = 0; i < 100; i++) { widgets.add(getRow(i)); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Sample App"), ), body: ListView(children: widgets), ); } Widget getRow(int i) { return GestureDetector( child: Padding( padding: EdgeInsets.all(10.0), child: Text("Row $i"), ), onTap: () { setState(() { widgets = List.from(widgets); widgets.add(getRow(widgets.length + 1)); print('row $i'); }); }, ); } } 


附加信息:


要创建列表,建议使用ListView.Builder

一个例子:


 import 'package:flutter/material.dart'; void main() { runApp(SampleApp()); } class SampleApp extends StatelessWidget { // This widget is the root of your application. @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(); for (int i = 0; i < 100; i++) { widgets.add(getRow(i)); } } @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 GestureDetector( child: Padding( padding: EdgeInsets.all(10.0), child: Text("Row $i"), ), onTap: () { setState(() { widgets.add(getRow(widgets.length + 1)); print('row $i'); }); }, ); } } 


问题:


什么是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> { // Create a text controller and use it to retrieve the current value. // of the TextField! final myController = TextEditingController(); @override void dispose() { // Clean up the controller when disposing of the Widget. myController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Retrieve Text Input'), ), body: Padding( padding: const EdgeInsets.all(16.0), child: TextField( controller: myController, ), ), floatingActionButton: FloatingActionButton( // When the user presses the button, show an alert dialog with the // text the user has typed into our text field. onPressed: () { return showDialog( context: context, builder: (context) { return AlertDialog( // Retrieve the text the user has typed in using our // TextEditingController content: Text(myController.text), ); }, ); }, tooltip: 'Show me the value!', child: Icon(Icons.text_fields), ), ); } } 


: 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 { // This widget is the root of your application. @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> { String _errorText; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Sample App"), ), body: Center( child: TextField( onSubmitted: (String text) { setState(() { if (!isEmail(text)) { _errorText = 'Error: This is not an email'; } else { _errorText = null; } }); }, decoration: InputDecoration(hintText: "This is a hint", errorText: _getErrorText()), ), ), ); } _getErrorText() { return _errorText; } bool isEmail(String emailString) { String emailRegexp = r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$'; RegExp regExp = RegExp(emailRegexp); return regExp.hasMatch(emailString); } } 

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!

Source: https://habr.com/ru/post/zh-CN477182/


All Articles