<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Flutter &#8211; wqh博客</title>
	<atom:link href="https://wangqianhong.com/tag/flutter/feed/" rel="self" type="application/rss+xml" />
	<link>https://wangqianhong.com</link>
	<description>和而不同</description>
	<lastBuildDate>Tue, 28 Oct 2025 06:25:09 +0000</lastBuildDate>
	<language>zh-CN</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://wangqianhong.com/wp-content/uploads/2020/09/cropped-1-1-1-32x32.png</url>
	<title>Flutter &#8211; wqh博客</title>
	<link>https://wangqianhong.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Flutter（十五）&#124; Web本地测试跨域问题</title>
		<link>https://wangqianhong.com/2021/12/flutter%ef%bc%88%e5%8d%81%e4%ba%94%ef%bc%89-web%e6%9c%ac%e5%9c%b0%e6%b5%8b%e8%af%95%e8%b7%a8%e5%9f%9f%e9%97%ae%e9%a2%98/</link>
					<comments>https://wangqianhong.com/2021/12/flutter%ef%bc%88%e5%8d%81%e4%ba%94%ef%bc%89-web%e6%9c%ac%e5%9c%b0%e6%b5%8b%e8%af%95%e8%b7%a8%e5%9f%9f%e9%97%ae%e9%a2%98/#respond</comments>
		
		<dc:creator><![CDATA[wqh_work]]></dc:creator>
		<pubDate>Thu, 30 Dec 2021 09:55:00 +0000</pubDate>
				<category><![CDATA[技术文章]]></category>
		<category><![CDATA[Flutter]]></category>
		<guid isPermaLink="false">https://wangqianhong.com/?p=2726</guid>

					<description><![CDATA[<p>Flutter的web在本地测试的时候，涉及到http跨域请求时，会报错： Access to XM&#8230; <a href="https://wangqianhong.com/2021/12/flutter%ef%bc%88%e5%8d%81%e4%ba%94%ef%bc%89-web%e6%9c%ac%e5%9c%b0%e6%b5%8b%e8%af%95%e8%b7%a8%e5%9f%9f%e9%97%ae%e9%a2%98/" class="more-link read-more" rel="bookmark">继续阅读 <span class="screen-reader-text">Flutter（十五）&#124; Web本地测试跨域问题</span><i class="fa fa-arrow-right"></i></a></p>
<p><a rel="nofollow" href="https://wangqianhong.com/2021/12/flutter%ef%bc%88%e5%8d%81%e4%ba%94%ef%bc%89-web%e6%9c%ac%e5%9c%b0%e6%b5%8b%e8%af%95%e8%b7%a8%e5%9f%9f%e9%97%ae%e9%a2%98/">Flutter（十五）| Web本地测试跨域问题</a>最先出现在<a rel="nofollow" href="https://wangqianhong.com">wqh博客</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<p>Flutter的web在本地测试的时候，涉及到http跨域请求时，会报错：</p>



<pre class="wp-block-preformatted">Access to XMLHttpRequest at '***' from origin 'http://localhost:6715' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.</pre>



<p>可以把Flutter升级到3.3.0版本或以上版本，就可以使用下面的命令关闭CORS：</p>



<pre class="wp-block-code"><code>flutter run -d chrome --web-browser-flag "--disable-web-security"</code></pre>



<p>那如何用局域网其他设备访问这个网站呢？例如PC（IP: 192.168.0.100）运行下面命令</p>



<pre class="wp-block-code"><code>flutter run -d chrome --web-browser-flag "--disable-web-security" --web-hostname 192.168.0.100 --web-port 13001</code></pre>



<p>运行起来之后，可以在其他设备的浏览器中直接输入<code>http://192.168.0.100:13001</code>就可以访问网站</p>



<p>如果你使用vscode调试，可以在launch.json中加入以下配置：</p>



<pre class="wp-block-preformatted"><code>"version": "0.2.0",
"configurations": [
    {
        "name": "test",
        "request": "launch",
        "type": "dart",
        "flutterMode": "debug",
        "args": [
            "-d",
            "chrome",
            "--web-renderer",
            "html",
            "--web-browser-flag",
            "--disable-web-security"
        ]
    }
]</code></pre>
<p><a rel="nofollow" href="https://wangqianhong.com/2021/12/flutter%ef%bc%88%e5%8d%81%e4%ba%94%ef%bc%89-web%e6%9c%ac%e5%9c%b0%e6%b5%8b%e8%af%95%e8%b7%a8%e5%9f%9f%e9%97%ae%e9%a2%98/">Flutter（十五）| Web本地测试跨域问题</a>最先出现在<a rel="nofollow" href="https://wangqianhong.com">wqh博客</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://wangqianhong.com/2021/12/flutter%ef%bc%88%e5%8d%81%e4%ba%94%ef%bc%89-web%e6%9c%ac%e5%9c%b0%e6%b5%8b%e8%af%95%e8%b7%a8%e5%9f%9f%e9%97%ae%e9%a2%98/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Flutter（十四）&#124; ListTile组件</title>
		<link>https://wangqianhong.com/2021/09/flutter%ef%bc%88%e5%8d%81%e5%9b%9b%ef%bc%89-listtile%e7%bb%84%e4%bb%b6/</link>
					<comments>https://wangqianhong.com/2021/09/flutter%ef%bc%88%e5%8d%81%e5%9b%9b%ef%bc%89-listtile%e7%bb%84%e4%bb%b6/#respond</comments>
		
		<dc:creator><![CDATA[wqh_work]]></dc:creator>
		<pubDate>Fri, 03 Sep 2021 08:01:06 +0000</pubDate>
				<category><![CDATA[技术文章]]></category>
		<category><![CDATA[Flutter]]></category>
		<guid isPermaLink="false">https://wangqianhong.com/?p=1718</guid>

					<description><![CDATA[<p>在日常的前端开发中，经常要显示下图中的标签栏： 之前我们学过使用Row，Column来拼出这样的标签&#8230; <a href="https://wangqianhong.com/2021/09/flutter%ef%bc%88%e5%8d%81%e5%9b%9b%ef%bc%89-listtile%e7%bb%84%e4%bb%b6/" class="more-link read-more" rel="bookmark">继续阅读 <span class="screen-reader-text">Flutter（十四）&#124; ListTile组件</span><i class="fa fa-arrow-right"></i></a></p>
<p><a rel="nofollow" href="https://wangqianhong.com/2021/09/flutter%ef%bc%88%e5%8d%81%e5%9b%9b%ef%bc%89-listtile%e7%bb%84%e4%bb%b6/">Flutter（十四）| ListTile组件</a>最先出现在<a rel="nofollow" href="https://wangqianhong.com">wqh博客</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<p>在日常的前端开发中，经常要显示下图中的标签栏：</p>



<div class="wp-block-image"><figure class="aligncenter size-large is-resized"><img loading="lazy" src="https://wangqianhong.com/wp-content/uploads/2022/09/flutter-2021-09-02.jpg" alt="" class="wp-image-1719" width="576" height="296"/></figure></div>



<p>之前我们学过使用Row，Column来拼出这样的标签栏，其实Flutter提供了标准的组件来完成这项功能，那就是ListTile.</p>



<h3>ListTile</h3>



<p>一起来看一下ListTile组件中经常用的属性：</p>



<pre class="wp-block-code"><code>ListTile ListTile({
  Key? key,
  Widget? leading,
  Widget? title,
  Widget? subtitle,
  Widget? trailing,
  bool isThreeLine = false,
  bool? dense,
  VisualDensity? visualDensity,
  ShapeBorder? shape,
  ListTileStyle? style,
  Color? selectedColor,
  Color? iconColor,
  Color? textColor,
  EdgeInsetsGeometry? contentPadding,
  bool enabled = true,
  void Function()? onTap,
  void Function()? onLongPress,
  MouseCursor? mouseCursor,
  bool selected = false,
  Color? focusColor,
  Color? hoverColor,
  FocusNode? focusNode,
  bool autofocus = false,
  Color? tileColor,
  Color? selectedTileColor,
  bool? enableFeedback,
  double? horizontalTitleGap,
  double? minVerticalPadding,
  double? minLeadingWidth,
})</code></pre>



<p>leading：对应上图中的1号位置。</p>



<p>title：对应上图中的2号位置。</p>



<p>subtitle：对应上图中的3号位置。</p>



<p>trailing：对应上图中的4号位置。</p>



<p>可以看到这4个属性传入的类型是Widget，这样就标签栏就可以很灵活的显示各种组件。</p>



<p>onTap：点击触发的回调函数。</p>



<p>onLongPress：长按触发的回调函数。</p>



<h3>random_password.dart</h3>



<p>接下来，我们来修改random_password中的组件，例如_switchLowercase函数，修改为：</p>



<pre class="wp-block-code"><code>Widget _switchLowercase() {
    return ListTile(
      title: Text(
        'lowercaseLetter'.tr,
        style: TextStyle(fontSize: 17),
      ),
      trailing: Switch(
        value: _lowercase,
        onChanged: (value) {
          setState(() {
            _lowercase = value;
          });
        },
      ),
    );
  }</code></pre>



<p>其他_switchUppercase、_switchNumber、_switchSpecial函数以此类推。</p>



<p><strong>但是修改_textLength函数时要注意，不能直接把TextField放到trailing中，因为TextField的宽度默认比trailing大</strong>。</p>



<p>所以如果要把TextField放到trailing中的时候，应用用SizeBox包裹一层：</p>



<pre class="wp-block-code"><code>SizedBox(
	width: 100,
	child: TextField(
	  controller: TextEditingController(text: _length),
	  textAlign: TextAlign.center,
	  decoration: InputDecoration(
		hintText: 'Enter length',
	  ),
	  keyboardType: TextInputType.number,
	  inputFormatters: &#91;
		FilteringTextInputFormatter.digitsOnly,
		LengthLimitingTextInputFormatter(2)
	  ],
	  onChanged: (text) {
		_length = text;
	  },
	),
),</code></pre>



<p>或者可以采用leading和title来达到目的：</p>



<pre class="wp-block-code"><code>Widget _textLength() {
	return ListTile(
	  leading: Text(
		'length'.tr,
		style: TextStyle(fontSize: 17),
	  ),
	  title: TextField(
		controller: TextEditingController(text: _length),
		textAlign: TextAlign.center,
		decoration: InputDecoration(
		  hintText: 'Enter length',
		),
		keyboardType: TextInputType.number,
		inputFormatters: &#91;
		  FilteringTextInputFormatter.digitsOnly,
		  LengthLimitingTextInputFormatter(2)
		],
		onChanged: (text) {
		  _length = text;
		},
	  ),
	);
}</code></pre>



<p></p>
<p><a rel="nofollow" href="https://wangqianhong.com/2021/09/flutter%ef%bc%88%e5%8d%81%e5%9b%9b%ef%bc%89-listtile%e7%bb%84%e4%bb%b6/">Flutter（十四）| ListTile组件</a>最先出现在<a rel="nofollow" href="https://wangqianhong.com">wqh博客</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://wangqianhong.com/2021/09/flutter%ef%bc%88%e5%8d%81%e5%9b%9b%ef%bc%89-listtile%e7%bb%84%e4%bb%b6/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Flutter（十三）&#124; Get包的Snackbar</title>
		<link>https://wangqianhong.com/2021/08/flutter%ef%bc%88%e5%8d%81%e4%b8%89%ef%bc%89-get%e5%8c%85%e7%9a%84snackbar/</link>
					<comments>https://wangqianhong.com/2021/08/flutter%ef%bc%88%e5%8d%81%e4%b8%89%ef%bc%89-get%e5%8c%85%e7%9a%84snackbar/#respond</comments>
		
		<dc:creator><![CDATA[wqh_work]]></dc:creator>
		<pubDate>Wed, 25 Aug 2021 02:25:41 +0000</pubDate>
				<category><![CDATA[技术文章]]></category>
		<category><![CDATA[Flutter]]></category>
		<guid isPermaLink="false">https://wangqianhong.com/?p=1714</guid>

					<description><![CDATA[<p>之前我们用过ScaffoldMessenger的showSnackBar，现在我们来学习Get包中的&#8230; <a href="https://wangqianhong.com/2021/08/flutter%ef%bc%88%e5%8d%81%e4%b8%89%ef%bc%89-get%e5%8c%85%e7%9a%84snackbar/" class="more-link read-more" rel="bookmark">继续阅读 <span class="screen-reader-text">Flutter（十三）&#124; Get包的Snackbar</span><i class="fa fa-arrow-right"></i></a></p>
<p><a rel="nofollow" href="https://wangqianhong.com/2021/08/flutter%ef%bc%88%e5%8d%81%e4%b8%89%ef%bc%89-get%e5%8c%85%e7%9a%84snackbar/">Flutter（十三）| Get包的Snackbar</a>最先出现在<a rel="nofollow" href="https://wangqianhong.com">wqh博客</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<p>之前我们用过ScaffoldMessenger的showSnackBar，现在我们来学习Get包中的snackbar</p>



<h3>snackbar</h3>



<p>把我们的<code>_snackBar</code>函数修改使用Get包的snackbar，代码如下：</p>



<pre class="wp-block-code"><code> Get.snackbar(
      '',
      'passwordClipboard'.tr,
      backgroundColor: Colors.blue,
      snackPosition: SnackPosition.BOTTOM,
      margin: const EdgeInsets.all(10),
      titleText: const Text(
        '',
        style: TextStyle(
          fontSize: 0,
        ),
      ),
      messageText: Text(
        'passwordClipboard'.tr,
        textAlign: TextAlign.center,
        style: const TextStyle(
          color: Colors.white,
          fontWeight: FontWeight.w800,
          fontSize: 20,
        ),
      ),
    );</code></pre>



<p>这里的第一行是title，我们暂时不用标题，但这个属性是必填项，我们用空字符串代替即可。</p>



<p>第二行是message，就是提示信息，这个属性也是必填项。</p>



<p>snackPosition：提示框显示位置，这里我们显示在底部。</p>



<p>backgroundColor：就是提示框的背景颜色。</p>



<p>titleText：对标题内容的属性进行修改，如果不用标题就把字体大小设为0，如果这里不设为0，即使标题是空字符串也是占用空间，影响美观。</p>



<p>messageText：对信息内容的属性进行修改。</p>



<h3>closeCurrentSnackbar</h3>



<p>对应之前的<code>removeCurrentSnackBar</code>，这里用<code>Get.closeCurrentSnackbar()</code>代替。</p>



<p>完成之后我们就可以跑起来试一下，效果和之前的ScaffoldMessenger方法是一样的。</p>
<p><a rel="nofollow" href="https://wangqianhong.com/2021/08/flutter%ef%bc%88%e5%8d%81%e4%b8%89%ef%bc%89-get%e5%8c%85%e7%9a%84snackbar/">Flutter（十三）| Get包的Snackbar</a>最先出现在<a rel="nofollow" href="https://wangqianhong.com">wqh博客</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://wangqianhong.com/2021/08/flutter%ef%bc%88%e5%8d%81%e4%b8%89%ef%bc%89-get%e5%8c%85%e7%9a%84snackbar/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Flutter（十二）&#124; Get包的国际化</title>
		<link>https://wangqianhong.com/2021/08/flutter%ef%bc%88%e5%8d%81%e4%b8%80%ef%bc%89-%e7%94%a8get%e5%8c%85%e5%9b%bd%e9%99%85%e5%8c%96/</link>
					<comments>https://wangqianhong.com/2021/08/flutter%ef%bc%88%e5%8d%81%e4%b8%80%ef%bc%89-%e7%94%a8get%e5%8c%85%e5%9b%bd%e9%99%85%e5%8c%96/#comments</comments>
		
		<dc:creator><![CDATA[wqh_work]]></dc:creator>
		<pubDate>Fri, 20 Aug 2021 07:10:26 +0000</pubDate>
				<category><![CDATA[技术文章]]></category>
		<category><![CDATA[Flutter]]></category>
		<category><![CDATA[随机密码生成器]]></category>
		<guid isPermaLink="false">https://wangqianhong.com/?p=1703</guid>

					<description><![CDATA[<p>最近在Flutter的开发中，学习使用了get（https://pub.flutter-io.cn/&#8230; <a href="https://wangqianhong.com/2021/08/flutter%ef%bc%88%e5%8d%81%e4%b8%80%ef%bc%89-%e7%94%a8get%e5%8c%85%e5%9b%bd%e9%99%85%e5%8c%96/" class="more-link read-more" rel="bookmark">继续阅读 <span class="screen-reader-text">Flutter（十二）&#124; Get包的国际化</span><i class="fa fa-arrow-right"></i></a></p>
<p><a rel="nofollow" href="https://wangqianhong.com/2021/08/flutter%ef%bc%88%e5%8d%81%e4%b8%80%ef%bc%89-%e7%94%a8get%e5%8c%85%e5%9b%bd%e9%99%85%e5%8c%96/">Flutter（十二）| Get包的国际化</a>最先出现在<a rel="nofollow" href="https://wangqianhong.com">wqh博客</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<p>最近在Flutter的开发中，学习使用了get（<a href="https://pub.flutter-io.cn/packages/get" target="_blank" rel="noreferrer noopener">https://pub.flutter-io.cn/packages/get</a>），关于get包的详细功能有机会再和大家展开，这里主要介绍一下get包的国际化功能。</p>



<h3>localizations.dart</h3>



<p>在根目录下面新建一个<code>localizations.dart</code>文件，这个文件就是把之前app_en.arb和app_zh.arb合成一个文件，代码如下 ：</p>



<pre class="wp-block-code"><code>import 'package:get/get.dart';

class AppLocalizations extends Translations {
  @override
  Map&lt;String, Map&lt;String, String>> get keys => {
        'zh_CN': {
          "generate": "生成",
          "lowercaseLetter": "小写字母",
          "uppercaseLetter": "大写字母",
          "arabicNumeral": "阿拉伯数字",
          "specialCharacter": "特殊字符",
          "length": "长度",
          "randomPassword": "随机密码",
          "passwordClipboard": "密码已经复制到剪贴板"
        },
        'en_US': {
          "generate": "Generate",
          "lowercaseLetter": "Lowercase Letter",
          "uppercaseLetter": "Uppercase Letter",
          "arabicNumeral": "Arabic Numeral",
          "specialCharacter": "Special Character",
          "length": "Length",
          "randomPassword": "Random Password",
          "passwordClipboard": "The password has been copied to the clipboard"
        }
      };
}
</code></pre>



<h3>main.dart</h3>



<p>修改main.dart文件，把build中的代码替换为下面的代码：</p>



<pre class="wp-block-code"><code>Widget build(BuildContext context) {
    SystemChrome.setPreferredOrientations(&#91;
      DeviceOrientation.portraitUp,
    ]);
    SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
      statusBarColor: Colors.transparent,
    ));
    return GetMaterialApp(
      debugShowCheckedModeBanner: false,
      translations: AppLocalizations(),
      locale: Get.deviceLocale,
      home: RandomPasswordPage(),
    );
  }</code></pre>



<p>GetMaterialApp是get包提供的一个函数。</p>



<p>translations：就是读取我们定义的AppLocalizations。</p>



<p>locale：就是读取系统的语言，这里使用Get.deviceLocale获取设备的语言，这样如果设备更改了语言，应用也会跟随变化，当然前提是我们的AppLocalizations类中定义了对应的语言。</p>



<h3>random_password.dart</h3>



<p>准备好语言环境之后，就可以修改对应的代码部分，把之前<code>AppLocalizations.of(context).randomPassword</code>直接替换成<code>'randomPassword'.tr</code>，这样当执行到tr关键字时，系统会去AppLocalizations寻找对应的key（这里就是randomPassword），然后显示对应的value值。</p>



<h3>get</h3>



<p>get包提供的国际化非常方便，而且直接融入了dart代码。</p>



<p>get的功能非常强大，应该是Flutter开发中必须掌握的package，后面我会把其他功能一一的展开介绍。</p>
<p><a rel="nofollow" href="https://wangqianhong.com/2021/08/flutter%ef%bc%88%e5%8d%81%e4%b8%80%ef%bc%89-%e7%94%a8get%e5%8c%85%e5%9b%bd%e9%99%85%e5%8c%96/">Flutter（十二）| Get包的国际化</a>最先出现在<a rel="nofollow" href="https://wangqianhong.com">wqh博客</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://wangqianhong.com/2021/08/flutter%ef%bc%88%e5%8d%81%e4%b8%80%ef%bc%89-%e7%94%a8get%e5%8c%85%e5%9b%bd%e9%99%85%e5%8c%96/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Flutter（十一）&#124; 使用Protobuf</title>
		<link>https://wangqianhong.com/2021/07/flutter%e4%bd%bf%e7%94%a8protobuf/</link>
					<comments>https://wangqianhong.com/2021/07/flutter%e4%bd%bf%e7%94%a8protobuf/#respond</comments>
		
		<dc:creator><![CDATA[wqh_work]]></dc:creator>
		<pubDate>Thu, 08 Jul 2021 04:02:16 +0000</pubDate>
				<category><![CDATA[技术文章]]></category>
		<category><![CDATA[Flutter]]></category>
		<guid isPermaLink="false">https://wangqianhong.com/?p=1560</guid>

					<description><![CDATA[<p>protoc支持生成dart格式，只支持proto3，官方地址：https://github.com&#8230; <a href="https://wangqianhong.com/2021/07/flutter%e4%bd%bf%e7%94%a8protobuf/" class="more-link read-more" rel="bookmark">继续阅读 <span class="screen-reader-text">Flutter（十一）&#124; 使用Protobuf</span><i class="fa fa-arrow-right"></i></a></p>
<p><a rel="nofollow" href="https://wangqianhong.com/2021/07/flutter%e4%bd%bf%e7%94%a8protobuf/">Flutter（十一）| 使用Protobuf</a>最先出现在<a rel="nofollow" href="https://wangqianhong.com">wqh博客</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<p>protoc支持生成dart格式，只支持proto3，官方地址：<a href="https://github.com/google/protobuf.dart" target="_blank" rel="noreferrer noopener">https://github.com/google/protobuf.dart</a></p>



<p>先要安装Flutter开发环境，还没有安装的小伙伴可以先看这篇文章</p>



<figure class="wp-block-embed-wordpress wp-block-embed is-type-wp-embed is-provider-wqh博客"><div class="wp-block-embed__wrapper">
<blockquote class="wp-embedded-content" data-secret="UPFKS9kHj6"><a href="https://wangqianhong.com/2021/03/flutter%ef%bc%88%e4%ba%8c%ef%bc%89-win%e5%bc%80%e5%8f%91%e7%8e%af%e5%a2%83%e6%90%ad%e5%bb%ba/">Flutter（二）| Win开发环境搭建</a></blockquote><iframe class="wp-embedded-content" sandbox="allow-scripts" security="restricted" title="《Flutter（二）| Win开发环境搭建》—wqh博客" src="https://wangqianhong.com/2021/03/flutter%ef%bc%88%e4%ba%8c%ef%bc%89-win%e5%bc%80%e5%8f%91%e7%8e%af%e5%a2%83%e6%90%ad%e5%bb%ba/embed/#?secret=UPFKS9kHj6" data-secret="UPFKS9kHj6" width="500" height="282" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"></iframe>
</div></figure>



<h3>安装protoc</h3>



<p>下载地址：<a href="https://github.com/protocolbuffers/protobuf/releases" target="_blank" rel="noreferrer noopener">https://github.com/protocolbuffers/protobuf/releases</a>，找到Latest版本，这里是protoc-3.19.4-win64.zip，下载完成之后，解压到对应目录。</p>



<h3>安装proto插件</h3>



<p>在cmd下面执行<code>flutter pub global activate protoc_plugin</code></p>



<h3>系统环境变量</h3>



<p>打开系统环境变量，在Path中新增下面路径：</p>



<pre class="wp-block-preformatted"><code>D:\Program Files\Protoc\bin</code> (这是protoc的bin目录)
<code>C:\Users\wqh\AppData\Local\Pub\Cache\bin</code> (这是proto插件的目录)
<code>D:\Program Files\Flutter\bin\cache\dart-sdk\bin</code> (这是dart的bin目录)</pre>



<h3>生成.dart文件</h3>



<p>在cmd下面打开proto文件所在目录，执行下面命令：</p>



<pre class="wp-block-code"><code>protoc --dart_out=. test.proto</code></pre>



<p>会自动生成test.pb.proto文件。</p>



<h3>安装dart插件</h3>



<p>打开项目的pubspec.yaml文件，在dependencies下面新增<code>protobuf: ^2.0.1</code>。</p>



<p>这样就可以在Flutter项目中使用Protobuf了。</p>



<h3>FAQ</h3>



<p>在使用protobuf过程中碰到<strong>Can&#8217;t load Kernel binary: Invalid SDK hash</strong>的问题，原来是使用flutter upgrade升级之后，需要重新执行一下<code>flutter pub global activate protoc_plugin</code>的命令</p>
<p><a rel="nofollow" href="https://wangqianhong.com/2021/07/flutter%e4%bd%bf%e7%94%a8protobuf/">Flutter（十一）| 使用Protobuf</a>最先出现在<a rel="nofollow" href="https://wangqianhong.com">wqh博客</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://wangqianhong.com/2021/07/flutter%e4%bd%bf%e7%94%a8protobuf/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Flutter（十）&#124; FAQ</title>
		<link>https://wangqianhong.com/2021/04/flutter%ef%bc%88%e5%8d%81%ef%bc%89-faq/</link>
					<comments>https://wangqianhong.com/2021/04/flutter%ef%bc%88%e5%8d%81%ef%bc%89-faq/#respond</comments>
		
		<dc:creator><![CDATA[wqh_work]]></dc:creator>
		<pubDate>Thu, 29 Apr 2021 04:18:50 +0000</pubDate>
				<category><![CDATA[技术文章]]></category>
		<category><![CDATA[Flutter]]></category>
		<category><![CDATA[随机密码生成器]]></category>
		<guid isPermaLink="false">https://wangqianhong.com/?p=1464</guid>

					<description><![CDATA[<p>记录一下在使用Flutter过程中经常碰到的一些问题？ 锁定App方向 在使用App的时候，有时候我&#8230; <a href="https://wangqianhong.com/2021/04/flutter%ef%bc%88%e5%8d%81%ef%bc%89-faq/" class="more-link read-more" rel="bookmark">继续阅读 <span class="screen-reader-text">Flutter（十）&#124; FAQ</span><i class="fa fa-arrow-right"></i></a></p>
<p><a rel="nofollow" href="https://wangqianhong.com/2021/04/flutter%ef%bc%88%e5%8d%81%ef%bc%89-faq/">Flutter（十）| FAQ</a>最先出现在<a rel="nofollow" href="https://wangqianhong.com">wqh博客</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<p>记录一下在使用Flutter过程中经常碰到的一些问题？</p>



<h3>锁定App方向</h3>



<p>在使用App的时候，有时候我们需要锁定App横屏或竖屏，可以修改main.dart文件中的build接口：</p>



<pre class="wp-block-code"><code>SystemChrome.setPreferredOrientations(&#91;
   DeviceOrientation.portraitUp,
]);</code></pre>



<p>DeviceOrientation一共有4个选项，可以多项：</p>



<pre class="wp-block-code"><code>enum DeviceOrientation {
  /// If the device shows its boot logo in portrait, then the boot logo is shown
  /// in &#91;portraitUp]. Otherwise, the device shows its boot logo in landscape
  /// and this orientation is obtained by rotating the device 90 degrees
  /// clockwise from its boot orientation.
  portraitUp,

  /// The orientation that is 90 degrees clockwise from &#91;portraitUp].
  ///
  /// If the device shows its boot logo in landscape, then the boot logo is
  /// shown in &#91;landscapeLeft].
  landscapeLeft,

  /// The orientation that is 180 degrees from &#91;portraitUp].
  portraitDown,

  /// The orientation that is 90 degrees counterclockwise from &#91;portraitUp].
  landscapeRight,
}</code></pre>



<h3>App标题栏</h3>



<p>App标题栏和手机最上面的状态栏使用同一颜色，也是修改main.dart文件中的build接口：</p>



<pre class="wp-block-code"><code>SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
    statusBarColor: Colors.transparent,
));</code></pre>



<h3>消除右上角Debug标志</h3>



<p>修改main.dart文件中的build接口，在MaterialApp下面新增一行代码：</p>



<pre class="wp-block-code"><code>debugShowCheckedModeBanner: false,</code></pre>



<h3>Release版</h3>



<p>flutter run默认是运行debug版本，如果要发布release版，使用下面的命令：</p>



<pre class="wp-block-preformatted"><code>flutter build apk</code> 打包apk
<code>flutter build web</code> 打包web版本</pre>



<p>apk所在目录build\app\outputs\apk\release\app-release.apk。</p>



<p>web所在目录build\web</p>



<h3>Bottom Overflow</h3>



<p>当打开屏幕键盘的时候，body和Scaffold会自行调整大小，如果手机屏幕太小，有可能导致Widget超出屏幕，报&#8221;bottom overflowed by pixels&#8221;的错误，可以修改build接口下的Scaffold：</p>



<pre class="wp-block-preformatted">home: Scaffold(
	<code>resizeToAvoidBottomInset: false,</code>
	...
),</pre>



<p>设置为false之后，再打开屏幕键盘，就不会报错了，但是有可能屏幕键盘会遮挡住文本输入框。</p>



<p>而且这个错误只会在debug版本中显示，在release版本中不会显示错误，所以大可不必处理。</p>



<h3>Gradle错误</h3>



<p>使用verbose查看详细日志</p>



<pre class="wp-block-code"><code>flutter run --verbose</code></pre>



<pre class="wp-block-preformatted">Exception in thread "main" java.net.ConnectException: Connection timed out: connect</pre>



<p>gradle-wrapper.properties配置文件中，通过distributionUrl的下载超时：</p>



<pre class="wp-block-preformatted">distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip</pre>



<p>我们可以从<a href="https://services.gradle.org/distributions/" target="_blank" rel="noreferrer noopener">https://services.gradle.org/distributions/</a>手动下载对应的包，然后指定路径即可：</p>



<pre class="wp-block-code"><code>distributionUrl=file:///D:/download/gradle-7.6.3-all.zip</code></pre>



<p>或者也可以把下载后的压缩包放到对应的下载路径，aocdy2d2z8kodnny3rsumj8i8是随机生成的：</p>



<pre class="wp-block-code"><code>C:\Users\登录账号\.gradle\wrapper\dists\gradle-7.6.3-all\aocdy2d2z8kodnny3rsumj8i8</code></pre>



<h3>Gradle编译慢</h3>



<p>使用阿里云镜像仓库：<a href="https://developer.aliyun.com/mvn/guide">https://developer.aliyun.com/mvn/guide</a></p>



<p>修改android\settings.gradle</p>



<pre class="wp-block-code"><code>repositories {
        maven { url 'https://maven.aliyun.com/repository/google' }
        maven { url 'https://maven.aliyun.com/repository/public' }
        maven { url 'https://maven.aliyun.com/repository/central' }
        maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }
        mavenLocal()
        // google()
        // mavenCentral()
        // gradlePluginPortal()
}</code></pre>



<p>然后执行命令</p>



<pre class="wp-block-preformatted"><code>cd .\android\</code>
<code>.\gradlew dependencies</code></pre>



<p id="block-625461bd-7fa1-4aa2-b104-b83fefde12d2">如果是新版本，可能文件名是settings.gradle.kts</p>



<pre class="wp-block-code"><code>repositories {
	maven { url = uri("https://maven.aliyun.com/repository/public") }
	maven { url = uri("https://mirrors.tencent.com/nexus/repository/maven-public/") }
	maven { url = uri("https://repo.huaweicloud.com/repository/maven/") }
	// google()
	// mavenCentral()
	// gradlePluginPortal()
}</code></pre>



<h3>设置VPN</h3>



<p>如果能后翻墙，也可以使用proxy</p>



<pre class="wp-block-preformatted"><code>$env:HTTP_PROXY="socks5://127.0.0.1:1080"</code>
<code>$env:HTTPS_PROXY="socks5://127.0.0.1:1080"</code>
或者
<code>$env:GRADLE_OPTS="-DsocksProxyHost=127.0.0.1 -DsocksProxyPort=1080"</code></pre>



<p>然后刷新依赖</p>



<pre class="wp-block-code"><code>.\gradlew.bat clean --refresh-dependencies --info</code></pre>



<h3>如何获取包名</h3>



<p>使用 aapt 命令（已安装的APK）</p>



<pre class="wp-block-preformatted">查看APK文件的包名
<code>aapt dump badging your-app.apk | findstr "package: name"</code>

或者使用 aapt2
<code>aapt2 dump packagename your-app.apk</code></pre>



<h3>SHA-1证书指纹</h3>



<p>使用&nbsp;keytool 命令</p>



<pre class="wp-block-preformatted">正式apk
<code>keytool -list -v -keystore upload-keystore.jks -alias upload</code>

debug apk
<code>keytool -list -v -keystore C:/Users/adminstrator/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android</code></pre>



<p></p>
<p><a rel="nofollow" href="https://wangqianhong.com/2021/04/flutter%ef%bc%88%e5%8d%81%ef%bc%89-faq/">Flutter（十）| FAQ</a>最先出现在<a rel="nofollow" href="https://wangqianhong.com">wqh博客</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://wangqianhong.com/2021/04/flutter%ef%bc%88%e5%8d%81%ef%bc%89-faq/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Flutter（九）&#124; 官方推荐的国际化</title>
		<link>https://wangqianhong.com/2021/04/flutter%ef%bc%88%e4%b9%9d%ef%bc%89-%e5%ae%98%e6%96%b9%e6%8e%a8%e8%8d%90%e7%9a%84%e5%9b%bd%e9%99%85%e5%8c%96/</link>
					<comments>https://wangqianhong.com/2021/04/flutter%ef%bc%88%e4%b9%9d%ef%bc%89-%e5%ae%98%e6%96%b9%e6%8e%a8%e8%8d%90%e7%9a%84%e5%9b%bd%e9%99%85%e5%8c%96/#respond</comments>
		
		<dc:creator><![CDATA[wqh_work]]></dc:creator>
		<pubDate>Fri, 23 Apr 2021 02:23:18 +0000</pubDate>
				<category><![CDATA[技术文章]]></category>
		<category><![CDATA[Flutter]]></category>
		<category><![CDATA[随机密码生成器]]></category>
		<guid isPermaLink="false">https://wangqianhong.com/?p=1448</guid>

					<description><![CDATA[<p>除了使用插件，官方推荐另一种国际化方法，链接如下：https://flutter.cn/docs/d&#8230; <a href="https://wangqianhong.com/2021/04/flutter%ef%bc%88%e4%b9%9d%ef%bc%89-%e5%ae%98%e6%96%b9%e6%8e%a8%e8%8d%90%e7%9a%84%e5%9b%bd%e9%99%85%e5%8c%96/" class="more-link read-more" rel="bookmark">继续阅读 <span class="screen-reader-text">Flutter（九）&#124; 官方推荐的国际化</span><i class="fa fa-arrow-right"></i></a></p>
<p><a rel="nofollow" href="https://wangqianhong.com/2021/04/flutter%ef%bc%88%e4%b9%9d%ef%bc%89-%e5%ae%98%e6%96%b9%e6%8e%a8%e8%8d%90%e7%9a%84%e5%9b%bd%e9%99%85%e5%8c%96/">Flutter（九）| 官方推荐的国际化</a>最先出现在<a rel="nofollow" href="https://wangqianhong.com">wqh博客</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<p>除了使用插件，官方推荐另一种国际化方法，链接如下：<a href="https://flutter.cn/docs/development/accessibility-and-localization/internationalization" target="_blank" rel="noreferrer noopener">https://flutter.cn/docs/development/accessibility-and-localization/internationalization</a></p>



<h3>l10n.yaml</h3>



<p>首先把上一节中的插件卸载掉，重新加载VSCode，删掉generated文件夹。</p>



<p>然后在根目录下面新建一个l10n.yaml文件，内容如下 ：</p>



<pre class="wp-block-code"><code>arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart</code></pre>



<p>arb文件还是放在lib/l10n下面，所以并没有删除之前的文件，但是需要把文件名修改为app_前缀，这里是app_en.arb和app_zh.arb。</p>



<p>如果之前没有l10n目录，需要手动创建目录及下面的arb文件。</p>



<p>执行消除命令<code>flutter clean</code>。</p>



<h3>pubspec.yaml</h3>



<p>修改pubspec.yaml文件：</p>



<pre class="wp-block-code"><code>flutter_localizations:
    sdk: flutter
intl: ^0.17.0

flutter:
  generate: true</code></pre>



<p>intl要使用最新版，可以通过链接查看最新版本：<a href="https://pub.dev/packages/intl/install" target="_blank" rel="noreferrer noopener">https://pub.dev/packages/intl/install</a></p>



<p>保存修改之后，可以看到.dart_tool目录下，自动生成了flutter_gen\gen_l10n：</p>



<pre class="wp-block-preformatted">├─flutter_gen
│ └─gen_l10n
│       └─app_localizations_en.dart
│       └─app_localizations_zh.dart
│       └─app_localizations.dart
│ └─pubspec.yaml</pre>



<p>注意：这些文件都是自动生成，不需要修改。</p>



<h3>main.dart</h3>



<p>文件生成之后，修改main.dart文件：</p>



<pre class="wp-block-code"><code>import 'package:flutter/material.dart';
import 'package:random_app/random/random_password.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      localizationsDelegates: AppLocalizations.localizationsDelegates,
      supportedLocales: AppLocalizations.supportedLocales,
      home: RandomPasswordPage(),
    );
  }
}
</code></pre>



<p>然后修改random_password.dart中需要国际化的地方，例如：</p>



<pre class="wp-block-preformatted">把<code>S.of(context)</code>或<code>S.current</code>全部修改为
<code>AppLocalizations.of(context)</code>

并引入包
import&nbsp;'package:flutter_gen/gen_l10n/app_localizations.dart';</pre>



<p>修改完之后，重新运行flutter run，可以看到实现了同样的效果，并且切换系统语言之后，界面也会变化。</p>



<p>相比这2种方法，个人更推荐第3种方法，使用Get包中的国际化</p>



<figure class="wp-block-embed-wordpress wp-block-embed is-type-wp-embed is-provider-wqh博客"><div class="wp-block-embed__wrapper">
<blockquote class="wp-embedded-content" data-secret="WHYiaMj6om"><a href="https://wangqianhong.com/2021/08/flutter%ef%bc%88%e5%8d%81%e4%b8%80%ef%bc%89-%e7%94%a8get%e5%8c%85%e5%9b%bd%e9%99%85%e5%8c%96/">Flutter（十二）| Get包的国际化</a></blockquote><iframe class="wp-embedded-content" sandbox="allow-scripts" security="restricted" title="《Flutter（十二）| Get包的国际化》—wqh博客" src="https://wangqianhong.com/2021/08/flutter%ef%bc%88%e5%8d%81%e4%b8%80%ef%bc%89-%e7%94%a8get%e5%8c%85%e5%9b%bd%e9%99%85%e5%8c%96/embed/#?secret=WHYiaMj6om" data-secret="WHYiaMj6om" width="500" height="282" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"></iframe>
</div></figure>
<p><a rel="nofollow" href="https://wangqianhong.com/2021/04/flutter%ef%bc%88%e4%b9%9d%ef%bc%89-%e5%ae%98%e6%96%b9%e6%8e%a8%e8%8d%90%e7%9a%84%e5%9b%bd%e9%99%85%e5%8c%96/">Flutter（九）| 官方推荐的国际化</a>最先出现在<a rel="nofollow" href="https://wangqianhong.com">wqh博客</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://wangqianhong.com/2021/04/flutter%ef%bc%88%e4%b9%9d%ef%bc%89-%e5%ae%98%e6%96%b9%e6%8e%a8%e8%8d%90%e7%9a%84%e5%9b%bd%e9%99%85%e5%8c%96/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Flutter（八）&#124; 用Intl插件国际化</title>
		<link>https://wangqianhong.com/2021/04/flutter%ef%bc%88%e5%85%ab%ef%bc%89-%e7%94%a8intl%e6%8f%92%e4%bb%b6%e5%9b%bd%e9%99%85%e5%8c%96/</link>
					<comments>https://wangqianhong.com/2021/04/flutter%ef%bc%88%e5%85%ab%ef%bc%89-%e7%94%a8intl%e6%8f%92%e4%bb%b6%e5%9b%bd%e9%99%85%e5%8c%96/#comments</comments>
		
		<dc:creator><![CDATA[wqh_work]]></dc:creator>
		<pubDate>Sat, 17 Apr 2021 09:39:31 +0000</pubDate>
				<category><![CDATA[技术文章]]></category>
		<category><![CDATA[Flutter]]></category>
		<category><![CDATA[随机密码生成器]]></category>
		<guid isPermaLink="false">https://wangqianhong.com/?p=1415</guid>

					<description><![CDATA[<p>当App上架的时候，面对不同国家的语言，需要用到国际化，在Flutter中有三种方法可以进行国际化，&#8230; <a href="https://wangqianhong.com/2021/04/flutter%ef%bc%88%e5%85%ab%ef%bc%89-%e7%94%a8intl%e6%8f%92%e4%bb%b6%e5%9b%bd%e9%99%85%e5%8c%96/" class="more-link read-more" rel="bookmark">继续阅读 <span class="screen-reader-text">Flutter（八）&#124; 用Intl插件国际化</span><i class="fa fa-arrow-right"></i></a></p>
<p><a rel="nofollow" href="https://wangqianhong.com/2021/04/flutter%ef%bc%88%e5%85%ab%ef%bc%89-%e7%94%a8intl%e6%8f%92%e4%bb%b6%e5%9b%bd%e9%99%85%e5%8c%96/">Flutter（八）| 用Intl插件国际化</a>最先出现在<a rel="nofollow" href="https://wangqianhong.com">wqh博客</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<p>当App上架的时候，面对不同国家的语言，需要用到国际化，在Flutter中有三种方法可以进行国际化，这一节介绍第一种。</p>



<h3>Flutter Intl插件</h3>



<p>打开VSCode，在扩展中探索<code>Intl</code>并安装，插件链接：<a href="https://marketplace.visualstudio.com/items?itemName=localizely.flutter-intl" target="_blank" rel="noreferrer noopener">https://marketplace.visualstudio.com/items?itemName=localizely.flutter-intl</a></p>



<p>安装好插件之后，按下Ctrl+Shift+P，在命令中输入intl，首先执行命令Flutter Intl: Initialize。</p>



<p>插件会在lib目录下，自动生成国际化所需要有文件。</p>



<pre class="wp-block-preformatted">├─generated
│ └─intl
│      └─messages_all.dart
│      └─messages_en.dart
│ └─l10n.dart
├─l10n
│ └─intl_en.arb
└─random</pre>



<p>l10n目录下的arb就是配置文件。</p>



<p>generated目录下的就是对应配置文件的dart文件。</p>



<h3>pubspec.yaml</h3>



<p>自动生成文件之后，需要引入国际化包。</p>



<p>修改pubspec.yaml文件，在dependencies下新增包：</p>



<pre class="wp-block-code"><code>flutter_localizations:
  sdk: flutter</code></pre>



<p>保存文件，等待Flutter自动下载包。</p>



<h3>intl_en.arb</h3>



<p>修改配置文件，配置需要进行翻译的文字内容：</p>



<pre class="wp-block-code"><code>{
	"generate": "Generate",
	"lowercaseLetter": "Lowercase Letter",
	"uppercaseLetter": "Uppercase Letter",
	"arabicNumeral": "Arabic Numeral",
	"specialCharacter": "Special Character",
	"length": "Length",
	"randomPassword": "Random Password",
	"passwordClipboard": "The password has been copied to the clipboard"
}</code></pre>



<p>当保存修改之后，可以看到插件会自动生成messages_en.dart文件：</p>



<pre class="wp-block-code"><code>class MessageLookup extends MessageLookupByLibrary {
  String get localeName => 'en';

  final messages = _notInlinedMessages(_notInlinedMessages);
  static _notInlinedMessages(_) => &lt;String, Function> {
    "arabicNumeral" : MessageLookupByLibrary.simpleMessage("Arabic Numeral"),
    "generate" : MessageLookupByLibrary.simpleMessage("Generate"),
    "length" : MessageLookupByLibrary.simpleMessage("Length"),
    "lowercaseLetter" : MessageLookupByLibrary.simpleMessage("Lowercase Letter"),
    "passwordClipboard" : MessageLookupByLibrary.simpleMessage("The password has been copied to the clipboard"),
    "randomPassword" : MessageLookupByLibrary.simpleMessage("Random Password"),
    "specialCharacter" : MessageLookupByLibrary.simpleMessage("Special Character"),
    "uppercaseLetter" : MessageLookupByLibrary.simpleMessage("Uppercase Letter")
  };
}</code></pre>



<p>注意：generated目录下的文件不需要去修改，只需要修改l10n目录下的arb文件即可。</p>



<h3>Add locale</h3>



<p>那如何新增一个语言呢？</p>



<p>按下Ctrl+Shift+P，在命令中输入intl，执行命令Flutter Intl: Add locale，在弹出的输入框中输入zh，表示要加入中文的支持。</p>



<p>插件会自动生成messages_zh.dart和intl_zh.arb文件，修改intl_zh.arb文件：</p>



<pre class="wp-block-code"><code>{
	"generate": "生成",
	"lowercaseLetter": "小写字母",
	"uppercaseLetter": "大写字母",
	"arabicNumeral": "阿拉伯数字",
	"specialCharacter": "特殊字符",
	"length": "长度",
	"randomPassword": "随机密码",
	"passwordClipboard": "密码已经复制到剪贴板"
}</code></pre>



<h3>应用国际化</h3>



<p>到这一步，国际化所需要的文件都已经准备好了，接下来修改代码使用。</p>



<p>首先修改main.dart文件：</p>



<pre class="wp-block-code"><code>import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:random_app/generated/l10n.dart';
import 'package:random_app/random/random_password.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      localizationsDelegates: const &#91;
        S.delegate,
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate,
      ],
      supportedLocales: S.delegate.supportedLocales,
      home: RandomPasswordPage(),
    );
  }
}
</code></pre>



<p>然后修改random_password.dart中需要国际化的地方，例如：</p>



<pre class="wp-block-preformatted"><code>title: Text('Random Password'),</code>
修改为
<code>title: Text(S.of(context).randomPassword),</code>

<code>'Generate',</code>
修改为
<code>S.current.generate,</code>

不要忘记引入包
<code>import&nbsp;'package:random_app/generated/l10n.dart';</code></pre>



<p>S类有2个方法可以调用get方法，是用of(context)还是current取决于接口中有没有传入context，其他的依此类推修改即可。</p>



<p>修改完成之后，运行flutter run查看，会发现界面已经显示成了中文；更神奇的是，当你把手机的系统语言改成英文的时候，界面也自动变成了英文。</p>



<p>在下一节中，我们将学习另外一种国际化的方法，不使用intl插件，也是官方推荐的方法</p>
<p><a rel="nofollow" href="https://wangqianhong.com/2021/04/flutter%ef%bc%88%e5%85%ab%ef%bc%89-%e7%94%a8intl%e6%8f%92%e4%bb%b6%e5%9b%bd%e9%99%85%e5%8c%96/">Flutter（八）| 用Intl插件国际化</a>最先出现在<a rel="nofollow" href="https://wangqianhong.com">wqh博客</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://wangqianhong.com/2021/04/flutter%ef%bc%88%e5%85%ab%ef%bc%89-%e7%94%a8intl%e6%8f%92%e4%bb%b6%e5%9b%bd%e9%99%85%e5%8c%96/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Flutter（七）&#124; Clipboard剪贴板</title>
		<link>https://wangqianhong.com/2021/04/flutter%ef%bc%88%e4%b8%83%ef%bc%89-clipboard%e5%89%aa%e8%b4%b4%e6%9d%bf/</link>
					<comments>https://wangqianhong.com/2021/04/flutter%ef%bc%88%e4%b8%83%ef%bc%89-clipboard%e5%89%aa%e8%b4%b4%e6%9d%bf/#respond</comments>
		
		<dc:creator><![CDATA[wqh_work]]></dc:creator>
		<pubDate>Wed, 07 Apr 2021 01:23:17 +0000</pubDate>
				<category><![CDATA[技术文章]]></category>
		<category><![CDATA[Flutter]]></category>
		<category><![CDATA[随机密码生成器]]></category>
		<guid isPermaLink="false">https://wangqianhong.com/?p=1410</guid>

					<description><![CDATA[<p>Clipboard剪贴板是App应用必不可少的一个功能。 Clipboard 密码生成之后，用户可能&#8230; <a href="https://wangqianhong.com/2021/04/flutter%ef%bc%88%e4%b8%83%ef%bc%89-clipboard%e5%89%aa%e8%b4%b4%e6%9d%bf/" class="more-link read-more" rel="bookmark">继续阅读 <span class="screen-reader-text">Flutter（七）&#124; Clipboard剪贴板</span><i class="fa fa-arrow-right"></i></a></p>
<p><a rel="nofollow" href="https://wangqianhong.com/2021/04/flutter%ef%bc%88%e4%b8%83%ef%bc%89-clipboard%e5%89%aa%e8%b4%b4%e6%9d%bf/">Flutter（七）| Clipboard剪贴板</a>最先出现在<a rel="nofollow" href="https://wangqianhong.com">wqh博客</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<p>Clipboard剪贴板是App应用必不可少的一个功能。</p>



<h3>Clipboard</h3>



<p>密码生成之后，用户可能需要复制粘贴，那如何把生成好的密码复制到剪贴板呢？</p>



<p>Flutter早就想到了这个问题，只需要使用Clipboard.setData就可以在剪贴板上设置数据。</p>



<p>修改_generatePassword中的setState，在生成密码之后，把密码设置到Clipboard：</p>



<pre class="wp-block-code"><code>setState(() {
  _password = String.fromCharCodes(tmpPassword);
  Clipboard.setData(ClipboardData(text: _password));
});</code></pre>



<p>重新run之后，当点击Generate按钮之后，不仅可以显示在文本框，还可以切换到基本应用中，复制粘贴新生成的密码。</p>



<h3>SnackBar</h3>



<p>在使用剪贴板的时候，经常会看到这样一句提示&#8217;密码已经复制到剪贴板&#8217;，用来提示用户。</p>



<p>在Flutter中，可以通过SnackBar来实现：</p>



<pre class="wp-block-code"><code>void _snackBar() {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        backgroundColor: Colors.blue,
        content: Text(AppLocalizations.of(context).passwordClipboard,
            textAlign: TextAlign.center),
      ),
    );
  }</code></pre>



<p>修改build中的MaterialApp，加入scaffoldMessengerKey管理：</p>



<pre class="wp-block-code"><code>@override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(AppLocalizations.of(context).randomPassword),
        centerTitle: true,
      ),
      body: Column(
        children: &lt;Widget>&#91;
          _switchLowercase(),
          _switchUppercase(),
          _switchNumber(),
          _switchSpecial(),
          _textLength(),
          _textShow(),
          _buttonGenerate(),
        ],
      ),
    );
  }</code></pre>



<p>热加载一个，就可以看到当点击Generate按钮的时候，在最下面会弹出一个提示框，提示用户密码已经复制到粘贴板，过了几秒之后，提示框自动消失。</p>



<h3>SnackBar优化</h3>



<p>在使用SnackBar中会发现一个问题，就是当连续点击Generate按钮的时候，SnackBar会一个个出来，显得很卡，这是因为SnackBar是用一个队列管理，每一次点击按钮的时候，setData都会在队列中插入一条数据，然后由管理器一个个显示。</p>



<p>可以在setData的时候，调用removeCurrentSnackBar立即删除之前的数据：</p>



<pre class="wp-block-code"><code>void _snackBar() {
    ScaffoldMessenger.of(context).removeCurrentSnackBar();
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        backgroundColor: Colors.blue,
        content: Text(AppLocalizations.of(context).passwordClipboard,
            textAlign: TextAlign.center),
      ),
    );
  }</code></pre>



<p>这样连续点击的时候，提示框会立即出现。</p>



<p>在下一节中，我们将学习如何使用国际化？</p>
<p><a rel="nofollow" href="https://wangqianhong.com/2021/04/flutter%ef%bc%88%e4%b8%83%ef%bc%89-clipboard%e5%89%aa%e8%b4%b4%e6%9d%bf/">Flutter（七）| Clipboard剪贴板</a>最先出现在<a rel="nofollow" href="https://wangqianhong.com">wqh博客</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://wangqianhong.com/2021/04/flutter%ef%bc%88%e4%b8%83%ef%bc%89-clipboard%e5%89%aa%e8%b4%b4%e6%9d%bf/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Flutter（六）&#124; 随机密码</title>
		<link>https://wangqianhong.com/2021/04/flutter%ef%bc%88%e5%85%ad%ef%bc%89-%e9%9a%8f%e6%9c%ba%e5%af%86%e7%a0%81/</link>
					<comments>https://wangqianhong.com/2021/04/flutter%ef%bc%88%e5%85%ad%ef%bc%89-%e9%9a%8f%e6%9c%ba%e5%af%86%e7%a0%81/#respond</comments>
		
		<dc:creator><![CDATA[wqh_work]]></dc:creator>
		<pubDate>Thu, 01 Apr 2021 09:00:41 +0000</pubDate>
				<category><![CDATA[技术文章]]></category>
		<category><![CDATA[Flutter]]></category>
		<category><![CDATA[随机密码生成器]]></category>
		<guid isPermaLink="false">https://wangqianhong.com/?p=1401</guid>

					<description><![CDATA[<p>随机程序的核心就是根据界面选项随机生成密码。 Random 基本上任何语言都会提供一个随机函数，Da&#8230; <a href="https://wangqianhong.com/2021/04/flutter%ef%bc%88%e5%85%ad%ef%bc%89-%e9%9a%8f%e6%9c%ba%e5%af%86%e7%a0%81/" class="more-link read-more" rel="bookmark">继续阅读 <span class="screen-reader-text">Flutter（六）&#124; 随机密码</span><i class="fa fa-arrow-right"></i></a></p>
<p><a rel="nofollow" href="https://wangqianhong.com/2021/04/flutter%ef%bc%88%e5%85%ad%ef%bc%89-%e9%9a%8f%e6%9c%ba%e5%af%86%e7%a0%81/">Flutter（六）| 随机密码</a>最先出现在<a rel="nofollow" href="https://wangqianhong.com">wqh博客</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<p>随机程序的核心就是根据界面选项随机生成密码。</p>



<h3>Random</h3>



<p>基本上任何语言都会提供一个随机函数，Dart也不例外，在dart:math包中定义了Random类：</p>



<pre class="wp-block-code"><code>/// A generator of random bool, int, or double values.
///
/// The default implementation supplies a stream of pseudo-random bits that are
/// not suitable for cryptographic purposes.
///
/// Use the &#91;Random.secure]() constructor for cryptographic purposes.
abstract class Random {
  /// Creates a random number generator.
  ///
  /// The optional parameter &#91;seed] is used to initialize the
  /// internal state of the generator. The implementation of the
  /// random stream can change between releases of the library.
  external factory Random(&#91;int? seed]);

  /// Creates a cryptographically secure random number generator.
  ///
  /// If the program cannot provide a cryptographically secure
  /// source of random numbers, it throws an &#91;UnsupportedError].
  external factory Random.secure();

  /// Generates a non-negative random integer uniformly distributed in the range
  /// from 0, inclusive, to &#91;max], exclusive.
  ///
  /// Implementation note: The default implementation supports &#91;max] values
  /// between 1 and (1&lt;&lt;32) inclusive.
  int nextInt(int max);

  /// Generates a non-negative random floating point value uniformly distributed
  /// in the range from 0.0, inclusive, to 1.0, exclusive.
  double nextDouble();

  /// Generates a random boolean value.
  bool nextBool();
}</code></pre>



<p>有了Random就可以写出随机密码程序了。</p>



<h3>_generatePassword</h3>



<p>定义一个名叫_generatePassword的接口，随机密码有很多种方法，下面是我写的方法，核心思想就是：</p>



<ol><li>根据需要引入的元素生成一个足够长的字符串，把这个字符串随机乱序。</li><li>再根据要生成的密码长度生成一个索引链表，把这个索引链表随机乱序。</li><li>遍历索引链表，根据索引取字符串中对应的字符</li></ol>



<p>内容如下：</p>



<pre class="wp-block-code"><code>static const String LowerCase = 'abcdefghijklmnopqistuvwxyz';
static const String UpperCase = 'ABCDEFGHIJKLMNOPQISTUVWXYZ';
static const String ArabicNumeral = '01234567890123456789';
static const String SpeicalCharacter = '!@#%^&amp;*()-+=|&#91;]~&lt;>.';

void _generatePassword() {
    int length = int.parse(_length);
    if (length &lt;= 0 || (!_lowercase &amp;&amp; !_uppercase &amp;&amp; !_number &amp;&amp; !_speical)) {
      return;
    }

    List&lt;int> tmpPassword = &#91;];
    List&lt;int> tmpProp = &#91;];
    List&lt;int> propStr = &#91;];

    Random random = Random(DateTime.now().millisecondsSinceEpoch);

    if (_lowercase) {
      propStr.addAll(LowerCase.runes.toList());
    }
    if (_uppercase) {
      propStr.addAll(UpperCase.runes.toList());
    }
    if (_number) {
      propStr.addAll(ArabicNumeral.runes.toList());
    }
    if (_speical) {
      propStr.addAll(SpeicalCharacter.runes.toList());
    }

    while (propStr.length &lt; length * 2) {
      List&lt;int> tmpList = List.from(propStr);
      propStr.addAll(tmpList);
    }

    propStr.shuffle(random);
    for (int i = 0; i &lt; propStr.length; i++) {
      tmpProp.add(i);
    }
    tmpProp.shuffle(random);
    for (int item in tmpProp.getRange(0, length)) {
      tmpPassword.add(propStr&#91;item]);
    }
    setState(() {
      _password = String.fromCharCodes(tmpPassword);
    });
  }</code></pre>



<p>Random random = Random(DateTime.now().millisecondsSinceEpoch) 使用当前时间做为随机种子。</p>



<p>根据Switch开关的值，来引入对应的密码元素。</p>



<p>runes 是dart中表示utf-32格式字符的编码单元，可以把字符串变成List，有了List就可以使用自带的shuffle接口，shuffle可以把一个List随机打乱顺序。</p>



<p>while (propStr.length &lt; length * 2) 是为了防止引入的元素少于密码生成长度引起的Bug，例如如果用户只引入小写字母来生成长度99的密码，小写字母只有26个长度，所以我们要把小写字母扩展到比99更长。</p>



<p>propStr.shuffle(random) 随机字符串。</p>



<p>tmpProp.shuffle(random) 随机索引。</p>



<p>tmpPassword.add(propStr[item]) 根据索引取字符串中的字符。</p>



<p>setState 把生成的密码放到_password中保存。</p>



<h3>onPressed</h3>



<p>修改_buttonGenerate接口中的onPressed，点击按钮时，调用_generatePassword接口：</p>



<pre class="wp-block-code"><code>onPressed: () {
  _generatePassword();
},</code></pre>



<p>运行热加载，点击Generate按钮，就可以在文本框中看到生成的随机密码。</p>



<p>在下一切中，我们将学习如何使用剪贴板复制粘贴生成的密码？</p>
<p><a rel="nofollow" href="https://wangqianhong.com/2021/04/flutter%ef%bc%88%e5%85%ad%ef%bc%89-%e9%9a%8f%e6%9c%ba%e5%af%86%e7%a0%81/">Flutter（六）| 随机密码</a>最先出现在<a rel="nofollow" href="https://wangqianhong.com">wqh博客</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://wangqianhong.com/2021/04/flutter%ef%bc%88%e5%85%ad%ef%bc%89-%e9%9a%8f%e6%9c%ba%e5%af%86%e7%a0%81/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
