[在码头旁]颤抖。第5部分。对于Xamarin.Forms开发人员

这是Flutter文档的简要解释的最后一部分,这将对Xamarin.Forms开发人员很有用。鉴于当前的情况,现在该学习一些新知识了!通过削减,您可以为自己找到足够的信息,以评估从一个跨平台框架迁移到另一个框架是否值得,以及花费多长时间。



如果这些信息还不够,或者您有特定平台的本机开发经验,那么我建议您查看其他部分:

Flutter。第1部分。适用于Android开发人员
Flutter。第2部分。
面向iOS开发人员Flutter。第3部分。面向React Native
Flutter 开发人员第4部分针对Flutter Web开发人员
第5部分。对于Xamarin.Forms开发人员

内容:


  1. 项目

  2. Views



  3. Async UI





  4. Layouts



  5. ListView

  6. 文本

  7. Flutter插件

  8. 平台特定的代码

  9. 调试

  10. 本地存储



项目


题:


入口在哪里?

回答:


功能main()

差异:


在Xamarin.Forms中是LoadApplication(new App());

例:


void main() {
  runApp(new MyApp());
}

题:


如何创建页面元素

回答:


Flutter本身没有PageElement的概念所有组件都是小部件。Flutter有2种小部件:StatelessWidgetStatefulWidget它们以相同的方式工作,唯一的区别在于渲染状态。

差异:


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

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

例:


StatelessWidget:
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: new MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

StatefulWidget:
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(
        // Take the value from the MyHomePage object that was created by
        // the App.build method, and use it to set the appbar title.
        title: new Text(widget.title),
      ),
      body: new Center(
        // Center is a layout widget. It takes a single child and positions it
        // in the middle of the parent.
        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),
      ),
    );
  }
}

观看次数


题:


如何安排小部件?相当于什么XAML文件?

回答:


在Flutter中,布局是使用小部件树直接在代码中完成的。

区别:


在Xamarin.Forms中,最常见的布局是在XAML.file中完成Flutter没有等效的功能。

附加信息:


小部件列表可以在此处查看

例:


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

题:


如何通过代码添加或删除小部件?

回答:


通过更新父窗口小部件的状态,然后重建窗口小部件树。

差异:


在Xamarin.Forms中,可以使用Content元素或方法Add()的属性来完成此操作Remove()

例:


class SampleApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Sample App',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new SampleAppPage(),
    );
  }
}

class SampleAppPage extends StatefulWidget {
  SampleAppPage({Key key}) : super(key: key);

  @override
  _SampleAppPageState createState() => new _SampleAppPageState();
}

class _SampleAppPageState extends State<SampleAppPage> {
  // Default value for toggle
  bool toggle = true;
  void _toggle() {
    setState(() {
      toggle = !toggle;
    });
  }

  _getToggleChild() {
    if (toggle) {
      return new Text('Toggle One');
    } else {
      return new CupertinoButton(
        onPressed: () {},
        child: new Text('Toggle Two'),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Sample App"),
      ),
      body: new Center(
        child: _getToggleChild(),
      ),
      floatingActionButton: new FloatingActionButton(
        onPressed: _toggle,
        tooltip: 'Update Text',
        child: new Icon(Icons.update),
      ),
    );
  }
}

题:


如何为小部件制作动画?

回答:


使用AnimationAnimationController

差异:


Xamarin.Forms使用ViewExtensions和类似FadeTo或的方法TranslateTo

附加信息:


在此处 阅读有关动画的更多信息

例:


import 'package:flutter/material.dart';

void main() {
  runApp(new FadeAppTest());
}

class FadeAppTest extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Fade Demo',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new MyFadeTest(title: 'Fade Demo'),
    );
  }
}

class MyFadeTest extends StatefulWidget {
  MyFadeTest({Key key, this.title}) : super(key: key);
  final String title;
  @override
  _MyFadeTest createState() => new _MyFadeTest();
}

class _MyFadeTest extends State<MyFadeTest> with TickerProviderStateMixin {
  AnimationController controller;
  CurvedAnimation curve;

  @override
  void initState() {
    controller = new AnimationController(duration: const Duration(milliseconds: 2000), vsync: this);
    curve = new CurvedAnimation(parent: controller, curve: Curves.easeIn);
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text(widget.title),
      ),
      body: new Center(
          child: new Container(
              child: new FadeTransition(
                  opacity: curve,
                  child: new FlutterLogo(
                    size: 100.0,
                  )))),
      floatingActionButton: new FloatingActionButton(
        tooltip: 'Fade',
        child: new Icon(Icons.brush),
        onPressed: () {
          controller.forward();
        },
      ),
    );
  }
}

题:


如何在屏幕上绘制?

回答:


使用CustomPaintCustomPainter类

差异:


Xamarin.Forms使用第三方SkiaSharp在Flutter中,直接从包装盒中使用Skia画布

例:


import 'package:flutter/material.dart';

void main() => runApp(new MaterialApp(home: new DemoApp()));

class DemoApp extends StatelessWidget {
  Widget build(BuildContext context) => new Scaffold(body: new Signature());
}

class Signature extends StatefulWidget {
  SignatureState createState() => new SignatureState();
}

class SignatureState extends State<Signature> {
  List<Offset> _points = <Offset>[];
  Widget build(BuildContext context) {
    return new GestureDetector(
      onPanUpdate: (DragUpdateDetails details) {
        setState(() {
          RenderBox referenceBox = context.findRenderObject();
          Offset localPosition =
          referenceBox.globalToLocal(details.globalPosition);
          _points = new List.from(_points)..add(localPosition);
        });
      },
      onPanEnd: (DragEndDetails details) => _points.add(null),
      child: new CustomPaint(painter: new SignaturePainter(_points), size: Size.infinite),
    );
  }
}

class SignaturePainter extends CustomPainter {
  SignaturePainter(this.points);
  final List<Offset> points;
  void paint(Canvas canvas, Size size) {
    var paint = new 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;
}

题:


如何改变透明度?

回答:


使用不透明度小部件

差异:


Xamarin.Forms用途不透明VisualElement在Flutter中,您只需要在Opacity中包装所需的小部件

题:


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

回答:


将它们组成一个(而不是继承)。

差异:


在Xamarin.Forms中,您可以从我们感兴趣的VisualElement继承并添加自己的逻辑。在Flutter中,小部件始终从StatelessWidgetStatefulWidget继承那些。您需要创建一个新的小部件,并将其中的必要小部件集用作参数或字段。

例:


class CustomButton extends StatelessWidget {
  final String label;

  CustomButton(this.label);

  @override
  Widget build(BuildContext context) {
    return new RaisedButton(onPressed: () {}, child: new Text(label));
  }
}

导航


题:


如何在屏幕之间导航?

回答:


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

差异:


Xamarin.Forms使用NavigationPage

Flutter有两种类似于NavigationPage的导航方法

  1. 映射名称以描述Route ;
  2. 直接导航到Route

导航器可以将()pop()送到您指定的路线。

例:


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

题:


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

回答:


通过MethodChannelurl_launcher插件使用本机实现

异步用户界面


题:


相当于Device.BeginInvokeOnMainThread()如何异步执行代码?

回答:


Dart实现了一个在Isolates上运行的单线程执行模型对于异步执行,请使用C#熟悉的async / await。

例:


完成请求并返回结果以更新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);
    });
  }
}

题:


如何发出网络请求?

回答:


使用http插件

例:


通过pubspec.yaml以下方式连接依赖项
dependencies:
  ...
  http: ^0.11.3+16

查询:
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

差异:


在Xamarin.Forms中,可以通过在中放置进度指示器直接完成此操作XAML

例:


import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

void main() {
  runApp(new SampleApp());
}

class SampleApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Sample App',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new SampleAppPage(),
    );
  }
}

class SampleAppPage extends StatefulWidget {
  SampleAppPage({Key key}) : super(key: key);

  @override
  _SampleAppPageState createState() => new _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 new Center(child: new CircularProgressIndicator());
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        appBar: new AppBar(
          title: new Text("Sample App"),
        ),
        body: getBody());
  }

  ListView getListView() => new ListView.builder(
      itemCount: widgets.length,
      itemBuilder: (BuildContext context, int position) {
        return getRow(position);
      });

  Widget getRow(int i) {
    return new Padding(padding: new EdgeInsets.all(10.0), child: new 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);
    });
  }
}

项目结构与资源


题:


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

回答:


在中assets

差异:


Xamarin.Forms没有用于平台资源的统一存储,因此您必须将它们分别存储在平台文件夹中。有颤动assets该文件夹assets可以位于项目中的任何位置,最重要的是,在文件中写入该文件夹的路径pubspec.yaml

附加信息:


比较Android和Flutter中图形资源的大小。
Android密度限定词抖动像素比
分辨率0.75倍
分辨率1.0倍
分辨率1.5倍
xhdpi2.0倍
xxhdpi3.0倍
xxxhdpi4.0倍
资源布局示例:
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");

直接用法示例asset
@override
Widget build(BuildContext context) {
  return Image.asset("images/my_image.png");
}

题:


在哪里存储字符串?如何定位它们?

回答:


Flutter目前没有特定的位置来存储字符串。建议将它们作为静态字段存储在单独的类中。插件用于本地化,例如flutter_localizationsl10n

差异:


Xamarin.Forms使用一个文件resx

例:


存储:
class Strings {
  static String welcomeMessage = "Welcome To Flutter";
}

使用:
new Text(Strings.welcomeMessage)

本土化:
dependencies:
  # ...
  flutter_localizations:
    sdk: flutter
  intl: "^0.15.6"

import 'package:flutter_localizations/flutter_localizations.dart';

new MaterialApp(
 localizationsDelegates: [
   // Add app-specific localization delegate[s] here.
   GlobalMaterialLocalizations.delegate,
   GlobalWidgetsLocalizations.delegate,
 ],
 supportedLocales: [
    const Locale('en', 'US'), // English
    const Locale('he', 'IL'), // Hebrew
    // ... other locales the app supports
  ],
  // ...
)

题:


项目文件在哪里?

回答:


Flutter没有在开发环境中打开该项目的项目文件。最接近的类似文件-- pubspec.yaml包含对插件和项目详细信息的依赖关系。

应用程序生命周期


题:


如何处理生命周期事件?

回答:


使用WidgetsBindingdidChangeAppLifecycleState()方法

附加信息:


扑使用FlutterActivity Android中代码和FlutterAppDelegate在IOS;由于这一点,扑引擎使得处理状态改变不惹人可能的。但是,如果您仍然需要根据状态进行一些工作,则生命周期会略有不同:
  • 无效-此方法仅在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()));
}

版面


题:


什么是StackLayout的类似物

回答:


垂直方向StackLayout类似Column,水平方向为Row

例:


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

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

题:


网格 的模拟是什么?

回答:


GridView

例:


GridView.count(
  // Create a grid with 2 columns. If you change the scrollDirection to
  // horizontal, this would produce 2 rows.
  crossAxisCount: 2,
  // Generate 100 widgets that display their index in the List
  children: List.generate(100, (index) {
    return Center(
      child: Text(
        'Item $index',
        style: Theme.of(context).textTheme.headline,
      ),
    );
  }),
);

题:


ScrollView 的类似物是什么?

回答:


最接近的类似物是SingleChildScrollView但是Flutter最经常使用ListView来构建正在爬网的内容

例:


SingleChildScrollView:
@override
Widget build(BuildContext context) {
  return new SingleChildScrollView(
    child: new Text('Long Content'),
  );
}

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

手势处理


题:


如何处理点击?

回答:


如果窗口小部件支持方法onPressed,则可以使用它来处理单击。否则,这可以通过GestureDetector完成

例:


onPressed:
@override
Widget build(BuildContext context) {
  return new RaisedButton(
      onPressed: () {
        print("click");
      },
      child: new Text("Button"));
}

手势检测器:
class SampleApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        body: new Center(
      child: new GestureDetector(
        child: new FlutterLogo(
          size: 200.0,
        ),
        onTap: () {
          print("tap");
        },
      ),
    ));
  }
}

题:


如何处理手势?

回答:


使用GestureDetector他们可以执行以下操作:

点按



双击



长按



垂直拖曳



水平拖曳



例:


AnimationController controller;
CurvedAnimation curve;

@override
void initState() {
  controller = new AnimationController(duration: const Duration(milliseconds: 2000), vsync: this);
  curve = new CurvedAnimation(parent: controller, curve: Curves.easeIn);
}

class SampleApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        body: new Center(
          child: new GestureDetector(
            child: new RotationTransition(
                turns: curve,
                child: new FlutterLogo(
                  size: 200.0,
                )),
            onDoubleTap: () {
              if (controller.isCompleted) {
                controller.reverse();
              } else {
                controller.forward();
              }
            },
        ),
    ));
  }
}

ListView和适配器


题:


什么是ListView的类似物

回答:


ListView

差异:


在Xamarin.Forms中,您需要创建一个ViewCell和(通常是)一个DataTemplateSelector并将它们传递给ListView在Flutter中,您只需要传递要显示的小部件列表即可。

例:


import 'package:flutter/material.dart';

void main() {
  runApp(new SampleApp());
}

class SampleApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Sample App',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new SampleAppPage(),
    );
  }
}

class SampleAppPage extends StatefulWidget {
  SampleAppPage({Key key}) : super(key: key);

  @override
  _SampleAppPageState createState() => new _SampleAppPageState();
}

class _SampleAppPageState extends State<SampleAppPage> {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Sample App"),
      ),
      body: new ListView(children: _getListData()),
    );
  }

  _getListData() {
    List<Widget> widgets = [];
    for (int i = 0; i < 100; i++) {
      widgets.add(new Padding(padding: new EdgeInsets.all(10.0), child: new Text("Row $i")));
    }
    return widgets;
  }
}

题:


如何确定单击了哪个元素?

回答:


在Flutter中,元素小部件必须自己处理其单击。

差异:


Xamarin.Forms最常使用ItemTapped

例:


import 'package:flutter/material.dart';

void main() {
  runApp(new SampleApp());
}

class SampleApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Sample App',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new SampleAppPage(),
    );
  }
}

class SampleAppPage extends StatefulWidget {
  SampleAppPage({Key key}) : super(key: key);

  @override
  _SampleAppPageState createState() => new _SampleAppPageState();
}

class _SampleAppPageState extends State<SampleAppPage> {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Sample App"),
      ),
      body: new ListView(children: _getListData()),
    );
  }

  _getListData() {
    List<Widget> widgets = [];
    for (int i = 0; i < 100; i++) {
      widgets.add(new GestureDetector(
        child: new Padding(
            padding: new EdgeInsets.all(10.0),
            child: new Text("Row $i")),
        onTap: () {
          print('row tapped');
        },
      ));
    }
    return widgets;
  }
}

题:


如何动态更新ListView

回答:


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

差异:


Xamarin.Forms为此使用了ItemsSource在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');
        });
      },
    );
  }
}

文本


题:


如何使用自定义字体?

回答:


您只需要将字体文件放在一个文件夹中(以自己的名字命名)并指出其路径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'),
      ),
    ),
  );
}

题:


如何设置文字小部件的样式?

回答:


使用参数:

  • 颜色;
  • 装饰;
  • 装饰颜色;
  • 装饰风格;
  • 字体系列;
  • 字体大小;
  • 字体样式;
  • fontWeight;
  • hashCode;
  • 高度;
  • 继承;
  • 字母间距;
  • textBaseline;
  • wordSpacing。

题:


占位符 等于多少?

回答:


hintText财产InputDecoration

例:


body: new Center(
  child: new TextField(
    decoration: new 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第一方插件

平台特定的代码


题:


如何确定代码在哪个平台上运行?

回答:


platformThemePlatform类中 使用字段

例:


领域 platform:
if (Theme.of(context).platform == TargetPlatform.iOS) {
  return 'iOS';
} else if (Theme.of(context).platform == TargetPlatform.android) {
  return 'android';
} else if (Theme.of(context).platform == TargetPlatform.fuchsia) {
  return 'fuchsia';
} else {
  return 'not recognised ';
}

平台类别:
if (Platform.isIOS) {
  return 'iOS';
} else if (Platform.isAndroid) {
  return 'android';
} else if (Platform.isFuchsia) {
  return 'fuchsia';
} else {
  return 'not recognised ';
}

题:


如何调用本机平台代码?

回答:


通过MethodChannel

附加信息:


更多细节在这里

调试


题:


有哪些调试应用程序的工具?

回答:


开发工具

题:


怎么办hot reload

回答:


如果应用程序是从IntelliJ IDE,Android Studio或VSCode启动的,则可以通过组合⌘s/ctrl-s或单击图标来启动hot reload如果从终端启动,则输入字母r

题:


如何访问应用程序中的开发人员菜单?

回答:


如果是从IDE启动的,则使用IDE工具。如果从控制台,则使用h。

附加信息:


命令完整列表:

法案终端团队功能和领域
小部件层次wdebugDumpApp()
渲染树ŤdebugDumpRenderTree()
层数大号debugDumpLayerTree()
辅助功能S(遍历顺序)或U(逆向命中测试顺序)debugDumpSemantics()
小部件检查器一世WidgetsApp.showWidgetInspectorOverride
显示施工线pdebugPaintSizeEnabled
模拟不同的操作系统ØdefaultTargetPlatform
性能PWidgetsApp。showPerformanceOverlay
截图flutter.pngs
申请关闭q

本地存储


题:


如何key-value在应用程序中存储数据?

回答:


使用shared_preferences插件

差异:


在Xamarin.Forms中使用Xam.Plugins.Settings

例:


连接依赖性:
dependencies:
  flutter:
    sdk: flutter
  shared_preferences: ^0.4.3

使用:
SharedPreferences prefs = await SharedPreferences.getInstance();
_counter = prefs.getInt('counter');
prefs.setInt('counter', ++_counter);
setState(() {
  _counter = _counter;
});

题:


如何存储复杂数据?

回答:


使用sqflitehive等数据库插件

这也许是基本问题的答案。这结束了一系列的解释。我希望它们对所有对此框架感兴趣的开发人员都有用。也许您甚至开始写Flutter,并将您招募到Flutter开发人员的友好社区中。我将考虑使用新文章来发展社区,并使应用程序世界变得更美好。希望您的表格不会破坏Xamarin!

All Articles