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

Flutter已经写了许多文章。 每个月它变得越来越流行。 因此,我决定以简洁的问与答格式解释Flutter的官方文档。 我认为像我一样,许多人没有足够的空闲时间来详细研究他们仍然无法使用的框架的文档。

如果您想了解此框架的优点,并评估使用该框架需要付出的努力,欢迎您来关注。



内容:


  1. 观看次数

  2. 意向

  3. 异步用户界面

  4. 项目结构与资源

  5. 活动和片段

  6. 版面

  7. 手势和触摸事件处理。

  8. 列表视图和适配器

  9. 处理文字

  10. 输入形式

  11. Flutter插件

  12. 主题

  13. 数据库和本地存储

  14. 通知事项


观看次数


问题:


相当于Flutter中的View

答案是:


小部件

差异:


查看-实际上,屏幕上会显示什么。 调用Invalidate()来显示更改。

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

附加信息:


在Android本身上启动时,View在Widget的幕后。 Flutter包括材料组件库。 它包含实现材料设计准则的小部件。

问题:


如何更新小部件的显示?

答案是:


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

差异:


StatelessWidget具有不可变状态。 适用于显示文本,徽标等。 即 如果屏幕上的元素在整个显示时间内均不应更改,则适合您。 它也可以用作有状态窗口小部件的容器。

StatefulWidget具有状态状态,该状态存储有关当前状态的信息。 如果要在执行某些操作时更改屏幕上的元素(来自服务器的响应,用户单击按钮等),这是您的选择。

一个例子:


1)StatelessWidget-文字

Text( 'I like Flutter!', style: TextStyle(fontWeight: FontWeight.bold), ); 

2)StatefulWidget-当您单击(FloatingActionButton)按钮时,“文本”小部件中的文本将从“我喜欢Flutter”变为“ Flutter很棒!”。

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

问题:


如何用小部件布置屏幕? XML布局文件在哪里?

答案是:


Flutter没有用于屏幕的XML布局。 一切都直接在代码中在小部件树中排版。

一个例子:


 @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Sample App"), ), body: Center( child: MaterialButton( onPressed: () {}, child: Text('Hello'), padding: EdgeInsets.only(left: 10.0, right: 10.0), ), ), ); } 

Flutter中的所有默认小部件都可以在小部件目录中查看

问题:


在应用程序运行时如何在布局中添加或删除组件?

答案是:


通过一个函数,该函数将根据状态返回所需的小部件。

差异:


在Android上,您可以在ViewGroup中执行addView()或removeView()。 在Flutter中,这是不可能的,因为 小部件不变。 只有他们的状况可以改变。

一个例子:


如何通过单击FloatingActionButton将文本更改为Button。

 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> { //     bool toggle = true; void _toggle() { setState(() { toggle = !toggle; }); } _getToggleChild() { if (toggle) { return Text('Toggle One'); } else { return MaterialButton(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一起使用 ,它报告屏幕重绘。

差异:


在Android上,您可以使用animate()在XML中创建动画或为View创建动画。 在Flutter中,必须使用AnimationController用代码编写动画。

附加信息:


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

一个例子:


Flutter徽标的淡入淡出动画。

 import 'package:flutter/material.dart'; void main() { runApp(FadeAppTest()); } class FadeAppTest 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() { super.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(); }, ), ); } } 

问题:


如何使用画布

答案是:


Android和Flutter具有与Canvas相同的API, 他们使用相同的低级Skia引擎。

差异:


不行

附加信息:


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

在此处阅读更多信息: StackOverflow

一个例子:


 import 'package:flutter/material.dart'; void main() => runApp(MaterialApp(home: DemoApp())); class DemoApp extends StatelessWidget { Widget build(BuildContext context) => Scaffold(body: Signature()); } 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), ); } } 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; } 

问题:


如何创建自定义窗口小部件?

答案是:


在一个组件中组成小部件(而不是继承)。

差异:


在Android中,我们可以从我们感兴趣的View继承并添加我们自己的逻辑。 在Flutter中,这类似于ViewGroup,仅小部件始终从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"), ); } 

意向


问题:


颤振中的意图类似于什么?

答案是:


他不在那里。 在屏幕之间导航使用NavigatorRoute类。

要与外部组件(例如,相机或文件选择器)进行交互,可以在每个平台上使用插件或本机集成。 了解有关本机集成的更多信息: 开发软件包和插件

差异:


Flutter没有活动和片段之类的东西。 有导航器(Navigator)和路线(routes)。 Flutter应用程序类似于单活动应用程序,其中不同的屏幕代表不同的片段,而FragmentManager控制它们。 导航器原则上类似于FragmentManager。 它可以将()pop() 送到您指定的路线。 Route是一种片段,但在Flutter中,习惯将其与屏幕或页面进行比较。

在Android中,我们描述了我们可以在AndroidManifest.xml中导航的所有活动。

Flutter有两种方式:

  • 描述一个名为Route(MaterialApp)的地图
  • 直接导航到Route(WidgetApp)。

一个例子:


 void main() { runApp(MaterialApp( 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与应用程序的Android层进行交互

一个例子:


我们在AndroidManifest.xml中编写意图过滤器:

 <activity android:name=".MainActivity" android:launchMode="singleTop" android:theme="@style/LaunchTheme" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection" android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize"> <!-- ... --> <intent-filter> <action android:name="android.intent.action.SEND" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="text/plain" /> </intent-filter> </activity> 

我们在MainActivity中处理Intent,然后通过MethodChannel从Flutter中调用代码:

 package com.example.shared; import android.content.Intent; import android.os.Bundle; import java.nio.ByteBuffer; import io.flutter.app.FlutterActivity; import io.flutter.plugin.common.ActivityLifecycleListener; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; import io.flutter.plugins.GeneratedPluginRegistrant; public class MainActivity extends FlutterActivity { private String sharedText; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); GeneratedPluginRegistrant.registerWith(this); Intent intent = getIntent(); String action = intent.getAction(); String type = intent.getType(); if (Intent.ACTION_SEND.equals(action) && type != null) { if ("text/plain".equals(type)) { handleSendText(intent); // Handle text being sent } } new MethodChannel(getFlutterView(), "app.channel.shared.data").setMethodCallHandler( new MethodCallHandler() { @Override public void onMethodCall(MethodCall call, MethodChannel.Result result) { if (call.method.contentEquals("getSharedText")) { result.success(sharedText); sharedText = null; } } }); } void handleSendText(Intent intent) { sharedText = intent.getStringExtra(Intent.EXTRA_TEXT); } } 

当小部件开始绘制时,我们请求数据:

 import 'package:flutter/material.dart'; import 'package:flutter/services.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 Shared App Handler', 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> { static const platform = const MethodChannel('app.channel.shared.data'); String dataShared = "No data"; @override void initState() { super.initState(); getSharedText(); } @override Widget build(BuildContext context) { return Scaffold(body: Center(child: Text(dataShared))); } getSharedText() async { var sharedData = await platform.invokeMethod("getSharedText"); if (sharedData != null) { setState(() { dataShared = sharedData; }); } } } 

问题:


startActivityForResult()类似什么?

答案是:


await关键字和Future类的结果。

差异:


在Android中调用startActivityForResult()之后,我们需要在onActivityResult()中实现处理。 Flutter不需要执行任何操作,因为 导航器方法push()返回一个Future对象。

一个例子:


 Map coordinates = await Navigator.of(context).pushNamed('/location'); 

当我们在“ / location”屏幕上获得坐标时,执行pop():

 Navigator.of(context).pop({"lat":43.821757,"long":-79.226392}); 

异步用户界面


问题:


Flutter中的runOnUiThread()类似什么?

答案是:


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)。

差异:


在Android上开箱即用,您可以使用AsyncTask。 您需要在其中实现onPreExecute()doInBackground()onPostExecute() 。 在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; } } 

问题:


OkHttp的Flutter等价于什么?

答案是:


Flutter有自己的HTTP包

附加信息:


到目前为止,并不是所有的OkHttp功能都已在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); }); } } 

项目结构与资源


问题:


哪里存储不同分辨率的资源?

答案是:


在资产中。

差异:


在Android中,资源具有res文件夹并具有资产。 Flutter只有资产。 资产文件夹可以位于项目中的任何位置,最重要的是,在pubspec.yaml文件中写入路径。

附加信息:


比较Android和Flutter中图形资源的大小。
Android密度限定词抖动像素比
分辨率0.75倍
分辨率1.0倍
分辨率1.5倍
xhdpi2.0倍
xxhdpi3.0倍
xxxhdpi4.0倍
Flutter使用AssetManager或以Asset开头的专门类来使用代码中的资源。

一个例子:


AssetManager:

 val flutterAssetStream = assetManager.open("flutter_assets/assets/my_flutter_asset.png") 

资源位置:

 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.jpeg 

使用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) 

问题:


gradle文件的模拟是什么 如何添加依赖项?

答案是:


pubspec.yaml。

附加信息:


Flutter将程序集委托给本地Android和iOS构建器。 在Pub上查看Flutter的所有流行图书馆的列表。

活动和片段


问题:


什么是Flutter中的ActivityFragment

答案是:


Flutter中的所有内容都是小部件。 小部件扮演着活动和用于UI的片段的角色。 正如导航部分中提到的,导航的角色是“导航器”和“路线”。

附加信息:


Flutter对于Android开发人员:如何在Flutter中设计活动UI

问题:


如何处理生命周期事件?

答案是:


使用WidgetsBindingdidChangeAppLifecycleState()方法。

附加信息:


Flutter在本机代码中使用FlutterActivity,而Flutter引擎使处理状态更改尽可能不明显。 但是,如果您仍然需要根据状态进行一些工作,则生命周期会略有不同:

  • 无效-此方法仅在iOS中有效,在Android中没有类似功能;
  • 已暂停-与Android中的onPause()类似;
  • 恢复-类似于Android中的onPostResume();
  • 暂停-与Android中的onStop相似,在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())); } 

版面


问题:


什么是LinearLayout的类似物?

答案是:


表示水平, 表示垂直。

附加信息:


Flutter对于Android开发人员:如何在Flutter中设计LinearLayout?

一个例子:


 @override Widget build(BuildContext context) { return Row( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Text('Row One'), Text('Row Two'), Text('Row Three'), Text('Row Four'), ], ); } @override Widget build(BuildContext context) { return Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Text('Column One'), Text('Column Two'), Text('Column Three'), Text('Column Four'), ], ); } 

问题:


什么是RelativeLayout等效?

答案是:


小部件堆栈

更多详细信息:

堆栈溢出

问题:


ScrollView的类似物是什么?

答案是:


具有窗口小部件的ListView

一个例子:


 @override Widget build(BuildContext context) { return ListView( children: <Widget>[ Text('Row One'), Text('Row Two'), Text('Row Three'), Text('Row Four'), ], ); } 

问题:


如何处理人像和风景之间的过渡?

答案是:


如果AndroidManifest.xml包含FlutterView,则会处理翻转
android:configChanges =“方向| screenSize”

手势和触摸事件处理


问题:


如何在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(); } }, ), )); } } 

列表视图和适配器


问题:


Flutter中的ListView类似于什么?

答案是:


列表视图

差异:


Flutter不必考虑清理和重用项目(这是ListView / RecyclerView在Android中使用ViewHolder模式执行的操作)。

一个例子:


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

:


?

答案是:


GestureDetector .

一个例子:


 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(GestureDetector( child: Padding( padding: EdgeInsets.all(10.0), child: Text("Row $i")), onTap: () { print('row tapped'); }, )); } return widgets; } } 

:


ListView ?

答案是:


, setState() . , ListView.Builder , RecyclerView .

一个例子:


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 = <Widget>[]; @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 = <Widget>[]; @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'); }); }, ); } } 


:


?

答案是:


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

:


?

答案是:


:

  • color;
  • decoration;
  • decorationColor;
  • decorationStyle;
  • fontFamily;
  • fontSize;
  • fontStyle;
  • fontWeight;
  • hashCode;
  • height;
  • inherit;
  • letterSpacing;
  • textBaseline;
  • wordSpacing.


: Retrieve the value of a text field .

:


hint TextInput ?

答案是:


InputDecoration , .

一个例子:


 body: Center( child: TextField( decoration: InputDecoration(hintText: "This is a hint"), ) ) 

:


?

答案是:


InputDecoration .

一个例子:


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

Flutter


:


GPS?

答案是:


geolocator .

:


?

答案是:


image_picker .

:


Facebook?

答案是:


flutter_facebook_login .

:


Firebase?

答案是:


Firebase Flutter first party plugins .


:


() ?

答案是:


Flutter EventBus . : developing packages and plugins .

:


NDK?

答案是:


NDK- Flutter. Flutter .

Themes


:


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


:


Shared Preferences?

答案是:


Shared_Preferences plugin ( NSUserDefaults iOS ).

一个例子:


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

:


SQLite Flutter?

答案是:


SQFlite .


:


push-?

答案是:


Firebase_Messaging .

结论


. , , . « » . «-» . , ? 2016 Kotlin, - 2017. , , . , .
2016 Flutter Dart. , 2018 . . ! , , , . ( Google Fuchsia , , , Flutter ). — ! , — . 这就是我的全部。 Google Play!

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


All Articles