血液净化行业市场分析

第一节 血液净化行业概况

本文章引自:https://zhuanlan.zhihu.com/p/564622726

一、行业概况

血液净化是把患者的血液引出身体外并通过一种净化装置,除去其中某些致病物质,净化血液,达到治疗疾病的目的。血液净化的基础治疗方式包括血液透析(HD)、腹膜透析(PD)、血液滤过(HF)、血液透析滤过(HDF)、连续性肾脏替代治疗(CRRT)、血浆置换(PE)、血浆吸附和血液灌流(HP)等,以及由以上多种技术的联合应用。其中,血液透析是目前最常用、最重要的血液净化方法,是治疗急、慢性肾功能衰竭包括终末期肾病(ESRD)和某些急性药物、毒物中毒的有效方法。在临床应用的角度上看,出于保护患者血管的考虑,为达到最佳的治疗效果,对于有肾功能残余的患者,医生一般建议先腹膜透析再血透的治疗;对于完全失去肾功能的患者,一般建议血透治疗。

在对各种血液净化技术在临床使用、治疗效果、治疗费用等多维度进行分析,各种新的血液净化技术在原有的技术基础上不断发展和革新,但新旧技术相互之间不存在绝对的替代性,各种血液净化技术各有所长。血液透析因其治疗效果突出、广泛适用于各病情阶段的透析患者、报销比例较大,是终末期肾病等的最主要治疗方式,其它血液净化技术在治疗效果、适应症、经济可行性等各方面与其相互补充。

血液透析工作过程为:通过将体内血液引流至体外,经一个由无数根空心纤维组成的透析器中,血液与含机体浓度相似的电解质溶液(透析液)在一根根空心纤维内外,通过弥散/对流进行物质交换,清除体内的代谢废物、维持电解质和酸碱平衡;同时清除体内过多的水分。血液透析治疗过程中,主要涉及的医疗器械及耗材包括血液透析设备、透析器、水处理设备、体外循环血路、穿刺针、透析液、透析粉等。

区别于血液透析,连续性肾脏替代治疗(CRRT)的临床应用范围超出肾脏病的领域,从重症急性肾功能衰竭到多脏器功能障碍综合征(MODS)、全身炎症反应综合征(SIRS)、急性呼吸窘迫综合征(ARDS)、急性坏死性胰腺炎、挤压综合征、药物及毒物中毒以及严重心衰等,都具有良好的应用前景,成为各种危重病救治重要的支持措施。CRRT与机械通气和全胃肠外营养(TPN)是近年来重症监护病房(ICU)治疗中最重要的进展之一。CRRT治疗过程与血液透析类似,相应使用的耗材包括血液滤过器、体外循环血路、置换液/透析液、抗凝剂等。

二、行业特有的经营模式、利润水平的变动趋势和变动原因

  1、行业特有的经营模式

  我国血液净化行业主要采用一般销售模式销售血液净化设备与耗材产品,同时部分采用联动销售模式进行销售。联动销售是国内血液净化行业借鉴体外诊断行业而逐步采用的销售模式,即在销售血液净化设备的同时,向客户销售血液净化耗材,设备和耗材互相促进销售。联动销售的业务模式,进一步加速了我国血液净化行业企业向全产业链布局。

  在连锁透析医疗服务方面,首次接诊的透析患者需在指定公立医院办理特病卡后,方可在独立血液透析中心进行透析治疗。由于终末期肾病(ESRD)已被列入大病医保范畴,透析中心需与属地医保部门签署《医疗服务协议》,患者就诊信息与医保系统联网,方才享受医保报销服务。

  2、行业利润水平的变动趋势和变动原因

  由于肾病患者的病情特征——需定期进行血液净化医疗服务来清除患者体内多余水分、尿素等有害物质,血液透析医疗服务是刚性需求,患者需每周进行2次至3次血液透析。血液净化耗材作为血液透析过程中的一次性消耗品,相对于设备,其单次消耗价格相对较低,但持续需求量大,行业内企业的耗材类产品毛利率普遍较高,能为企业带来持续的利润。

  血液净化设备主要作为独立血液透析中心或医院透析科室的固定资产投入,折旧时间普遍较长,并且下游客户的议价能力较强,导致血液净化设备的毛利率与耗材相比相对偏低,但持续需求量也较大。随着血液净化行业联动销售的销售模式逐步兴起,促使了血液净化行业向全产业链布局。

  未来,血液净化行业内率先完成全产业链布局的企业,将能够持续获得利润。

  第二节 血液净化行业发展情况

  一、全球血液透析行业发展概况

  1、全球血液透析患者数量持续增加

  2、全球血液透析市场规模稳步增长

  在全球范围内,对于慢性肾病患者而言,血液透析一直也将持续成为主要的治疗方式。根据费森尤斯年报披露的预测和统计,近几年来,全球透析产业市场规模从2015年的668亿欧元稳步上升至2020年的820亿欧元,年均增长率约为4%左右,到2021年,透析市场规模会继续增长至830亿至850亿欧元左右。其中透析服务(包括药品)市场规模分占比在80%以上,而以透析机为主的透析产品市场规模则占比较小。预计未来几年全球血透行业市场规模将继续保持平稳增速,到2025年全球血透行业市场规模将接近1000亿欧元。

  欧洲血液透析服务以公立机构为主,而美国和日本以私人运营机构为主。目前全球提供血液透析服务的企业主要有费森尤斯医疗、DaVita以及百特等。

二、国内血液透析行业发展概况

  从全球来说,血液净化市场主要集中在美国、日本和欧洲等发达国家,占比高达70%以上,而我国血液净化市场起步较晚,技术水平相较于国外还有一定差距,但随着肾病患者人数不断增长以及血液透析费用医保报销比例的不断提高,我国血液净化市场规模将快速增长,同时随着国内厂家技术水平的不断进步,进口替代程度将会进一步加速。

  1、我国ESRD患者人数不断增长,且血液透析治疗率还远低于国际水平

  目前,我国存量透析治疗需求仍远未被满足。据全国血液净化病例信息登记系统(CNRDS)统计的资料显示:2016年接受血液透析治疗的患者人数为44.7万人,2019年底已达到63.3万人,年均复合增长率达12%。另一方面,我国新增终末期肾病患者人数也在迅速增长,从2016年至2019年底,新增患者数量的年复合增长率达21%左右,这是推动我国血液净化行业未来增长的重要因素之一。预计到2030年我国终末期肾病患者(ESRD)人数将突破300万人,而我国接受治疗的患者比率不到20%,如果未来尿毒症患者透析治疗率提高到国际平均水平37%,届时透析治疗人数将达到148万人,若接近或达到发达国家现在的平均治疗率75%,则透析治疗人数或将达到300万人以上。随着人均收入的增长及医保覆盖比例的增加,终末期肾病患者接受治疗比率也将不断提高,从而进一步推动血液净化行业的增长。血液净化市场前景广阔,拥有巨大的发掘空间。

  据医械研究院发布的《中国医疗器械蓝皮书(2019版)》估算,截至2020年年底,我国约有83万人(含血液透析和腹膜透析)接受血液净化治疗。

  2、血液透析服务市场规模已逾540亿,并将保持稳定增长态势

  根据蛋壳研究院最新统计数据,每位尿毒症患者的透析费用约为6-10万元/年,在此基础上,根据CNRDS公布的在透患者数量进行估算,我们可以得出近几年血液透析的市场规模。自2014年以来,在政策的驱动下,血液透析服务的市场规模连续多年保持双位数上涨,我们预计2020年整个血液透析服务市场规模已达到548亿左右,未来,随着医保覆盖面扩大、大病医保报销比例提高和分级诊断等医疗政策的进一步完善,新技术、新透析模式的创新发展和临床应用,终末期肾病患者多层次、个性化的需求必将得到满足,血液透析服务行业的市场规模将持续增长,并在2025年达到接近700亿。

根据蛋壳研究院的测算,2020年,透析机、透析器、其他耗材(粉液、管路等)及药品的市场规模分别在38亿元、66亿元、53亿元及64亿元左右。随着血透中心市场规模增加,透析设备及耗材的需求量也将持续上升,我们预计在2025年,整体上游产业链中包含的透析设备、药品及耗材的市场规模将接近300亿元

由于血液净化设备属于技术密集型、人才密集型的高技术行业,产品研发难度大、周期长、准入门槛高、监管严,需要投入大量的研发人员和研发资金,对企业的资金、技术和人才要求极高,内资企业市场参与较少。目前中国血液透析机市场份额仍主要依赖于进口产品血透机,主要为欧美系和日系血透机。根据蛋壳研究院统计的数据,2020年国内血液透析机销量,以费森尤斯30%的市场份额位居榜首,费森尤斯、贝朗、日机装、百特以及尼普洛5大外资厂商占据了国内血液透析市场80%-90%左右的市场份额,目前我国血液透析机的市场集中度高,市场份额主要被进口厂商占据,国产自主品牌的血液透析机市场占有率仅在10%-20%左右。血液透析设备领域亟需加速进口替代的步伐。

  3、国内透析中心数量远低于市场饱和率,未来血液净化设备与耗材产品需求将随透析中心数量增长而增加

  从2014年起,我国政府开始鼓励社会资本进入血液透析中心领域,血液透析中心向连锁化、集团化发展,为我国民营资本进入血透领域提供了非常好的政策契机。在此之前,受政策限制民营资本很难进入这一领域。根据CNRDS公布的数据,从2011年至2019年,我国血透中心数量从3,511家迅速攀升至6,362家,截至2019年,我国接受血液透析治疗的患者约有63.27万人,其中新增13.46万人,血透中心数量与接受治疗患者的数量均有较大增长。以美国为例,根据USRDS发布的2020年度报告的数据,2018年美国有784,883终末期肾病患者,其中61.7%的患者在血液透析中心接受治疗,1.3%接受家庭透析治疗。其中,美国国内共建有血液透析中心接近6,000家,平均每家透析中心有20台左右透析机。

  2020年,我国终末期肾病患者超过300万人,需要长期接受血液透析治疗,频率为每周2次至3次且每次透析时间需要四小时左右,预计中国需要3万家血液透析中心(以每家透析中心配置20台透析机估算)以满足目前的患者需求。但实际上我国血液透析中心数量离3万家的饱和容量还远远不够,市场具有很大发展空间。因此,随着血液透析中心的数量及规模逐渐扩增,血液净化设备及耗材产品的市场需求也会相应增加。

第三节 血液净化行业发展趋势

  1、血液净化全产业链布局将成为未来发展趋势

  中国血液透析服务供给不足,第三方独立的血液透析服务需求旺盛。受日益增长的血液透析患者治疗需求的影响,全国血液透析服务供给严重不足。血液透析服务对血液透析设备的依赖程度高,血透设备厂商具备进入服务市场的天然优势。对于其他非血液透析设备企业类的竞争者而言,独立血液透析中心投资属于重资产投资,需要配备大量的血液透析机,整体的进入成本更高和所需承担的风险更大。而对于这类上游血液透析设备厂商来说,他们可以利用其长期积累的品牌效应、稳定的产品供应能力,向下游的血液透析服务产业延伸,获得更大的协同效应。

  从全球市场来看,费森尤斯和百特公司等在血液净化设备、血液净化耗材以及血液透析医疗服务领域均实现了全产业链布局。国内企业如山外山、威高血液、宝莱特、三鑫医疗等也已开启产品系列化布局的进程。血液透析产品线的叠加可以产生协同效应,提升渠道的利用效率,加大下游客户一站式采购的便利性,强化终端客户的粘性。同时血液净化耗材属于高值消耗性医疗器械,研发技术难度远低于血液净化设备,具有投入见效快、效益好的特征,能持续给企业带来稳定收益。

  2、连锁血液透析中心医疗服务社区化

  连锁血液透析中心模式主要满足长期慢性病患者,连锁血液透析中心医疗服务社区化可方便病人就近透析,生活和工作,有利于病人回归社会。在独立血液透析中心社区化发展过程中,品牌影响力逐步提高,血液透析服务企业为了提高竞争力,从而快速分得较大块的“蛋糕”,建立标准化模式,实现连锁化经营模式将成为其未来发展的趋势。

  3、设备更加智能化、人性化

  随着技术的不断发展进步,血液净化设备的功能会更加完善,新的安全监测技术的应用会使设备的安全性大大提高,同时设备及耗材的集成度也会越来越高,操作将更加便捷。大数据与云技术将在血液净化设备中得到广泛应用,通过在线生命体征监测与疗效评价技术,建立患者病例数据库和机器运行数据库,通过大数据分析和处理,实现个性化、智能化的治疗,将大大提升患者疗效指标,提升患者生存质量。

flutter环境更新方法

常规更新方法:

运行:flutter upgrade,然后等待自动更新完成即可,

非常贵更新方法:

1.更新flutter代码库

找到flutter库的安装目录,运行git,同步拉取到最新的代码,

2.更新依赖

运行flutter doctor -v 自动下载更新依赖的库

Microsoft Windows [版本 10.0.17134.112]
(c) 2018 Microsoft Corporation。保留所有权利。

F:\JavaWorker\deheng\android\hemodialysis>flutter doctor -v
Checking Dart SDK version...
Downloading Dart SDK from Flutter engine 72c7a7567228cdaf8b7aa4a9e3d212ef9d4cc0ed...
Unzipping Dart SDK...
Building flutter tool...
Running pub upgrade...
Downloading package sky_engine...                            1.1s
Downloading common tools...                                  2.6s
Downloading windows-x64 tools...                             5.9s
Downloading android-arm-profile/windows-x64 tools...         1.2s
Downloading android-arm-release/windows-x64 tools...         0.8s
Downloading android-arm64-profile/windows-x64 tools...       0.9s
Downloading android-arm64-release/windows-x64 tools...       0.9s
Downloading android-x86 tools...                             4.8s
Downloading android-x64 tools...                             4.9s
Downloading android-arm tools...                             3.4s
Downloading android-arm-profile tools...                     1.8s
Downloading android-arm-release tools...                     2.2s
Downloading android-arm64 tools...                           1.9s
Downloading android-arm64-profile tools...                   1.8s
Downloading android-arm64-release tools...                   1.5s
Downloading android-arm-dynamic-profile tools...             1.9s
Downloading android-arm-dynamic-release tools...             2.8s
Downloading android-arm64-dynamic-profile tools...           2.0s
Downloading android-arm64-dynamic-release tools...           1.7s
[√] Flutter (Channel beta, v0.10.3-pre.3, on Microsoft Windows [Version 10.0.17134.112], locale zh-CN)
    • Flutter version 0.10.3-pre.3 at F:\ProgramFiles\flutter
    • Framework revision 233435c02a (42 minutes ago), 2018-11-30 10:00:27 +0800
    • Engine revision 72c7a75672
    • Dart version 2.1.0 (build 2.1.0-dev.9.4 f9ebf21297)

[√] Android toolchain - develop for Android devices (Android SDK 28.0.3)
    • Android SDK at F:\ProgramFiles\android\sdk
    • Android NDK location not configured (optional; useful for native profiling support)
    • Platform android-28, build-tools 28.0.3
    • ANDROID_HOME = F:\ProgramFiles\android\sdk
    • Java binary at: F:\ProgramFiles\android\Android Studio\jre\bin\java
    • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1136-b06)
    • All Android licenses accepted.

[√] Android Studio (version 3.2)
    • Android Studio at F:\ProgramFiles\android\Android Studio
    • Flutter plugin version 31.1.1
    • Dart plugin version 181.5656
    • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1136-b06)

[√] IntelliJ IDEA Ultimate Edition (version 2018.2)
    • IntelliJ at F:\ProgramFiles\JetBrains\IntelliJ IDEA 2018.2.4
    • Flutter plugin version 30.0.2
    • Dart plugin version 182.5124

[√] Connected device (2 available)
    • MI PAD 4  • 6074f4a0     • android-arm64 • Android 8.1.0 (API 27)
    • Che1 CL20 • f4e3fb7f933b • android-arm   • Android 4.4.4 (API 19)

• No issues found!

F:\JavaWorker\deheng\android\hemodialysis>


You need to add a widget, row, or prebuilt layout before you’ll see anything here. 🙂

按钮扁平化用法示例-buildFlatButton

Widget buildFlatButton() {
    return Align(
      alignment: const Alignment(0.0, -0.2),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: <Widget>[
          ButtonBar(
            mainAxisSize: MainAxisSize.min,
            children: <Widget>[
              FlatButton(
                child: const Text('FLAT BUTTON', semanticsLabel: 'FLAT BUTTON 1'),
                onPressed: () {
                  // Perform some action
                },
              ),
              const FlatButton(
                child: Text('DISABLED', semanticsLabel: 'DISABLED BUTTON 3',),
                onPressed: null,
              ),
            ],
          ),
          ButtonBar(
            mainAxisSize: MainAxisSize.min,
            children: <Widget>[
              FlatButton.icon(
                icon: const Icon(Icons.add_circle_outline, size: 18.0),
                label: const Text('FLAT BUTTON', semanticsLabel: 'FLAT BUTTON 2'),
                onPressed: () {
                  // Perform some action
                },
              ),
              FlatButton.icon(
                icon: const Icon(Icons.add_circle_outline, size: 18.0),
                label: const Text('DISABLED', semanticsLabel: 'DISABLED BUTTON 4'),
                onPressed: null,
              ),
            ],
          ),
        ],
      ),
    );
  }

 

 

 

 

 

  factory FlatButton.icon({
    Key key,
    @required VoidCallback onPressed,
    ValueChanged<bool> onHighlightChanged,
    ButtonTextTheme textTheme,
    Color textColor,
    Color disabledTextColor,
    Color color,
    Color disabledColor,
    Color highlightColor,
    Color splashColor,
    Brightness colorBrightness,
    EdgeInsetsGeometry padding,
    ShapeBorder shape,
    Clip clipBehavior,
    MaterialTapTargetSize materialTapTargetSize,
    @required Widget icon,
    @required Widget label,
  }) = _FlatButtonWithIcon;

 

 

 const FlatButton({
    Key key,
    @required VoidCallback onPressed,
    ValueChanged<bool> onHighlightChanged,
    ButtonTextTheme textTheme,
    Color textColor,
    Color disabledTextColor,
    Color color,
    Color disabledColor,
    Color highlightColor,
    Color splashColor,
    Brightness colorBrightness,
    EdgeInsetsGeometry padding,
    ShapeBorder shape,
    Clip clipBehavior = Clip.none,
    MaterialTapTargetSize materialTapTargetSize,
    @required Widget child,
  }) : super(
         key: key,
         onPressed: onPressed,
         onHighlightChanged: onHighlightChanged,
         textTheme: textTheme,
         textColor: textColor,
         disabledTextColor: disabledTextColor,
         color: color,
         disabledColor: disabledColor,
         highlightColor: highlightColor,
         splashColor: splashColor,
         colorBrightness: colorBrightness,
         padding: padding,
         shape: shape,
         clipBehavior: clipBehavior,
         materialTapTargetSize: materialTapTargetSize,
         child: child,
      );

 

7 of 7 in the series: Fltter移动开发相关

免费作图工具

本页收集一些免费的工具,创建漂亮的图表。

ProcessOn

在线版:https://www.processon.com/

yEd

桌面版:https://www.yworks.com/products/yed

在线版:https://www.yworks.com/products/yed-live

Pencil

桌面版:https://pencil.evolus.vn/

Dia

桌面版:http://dia-installer.de/

Inkscape

网址:https://inkscape.org/

Draw.io

在线版:https://www.draw.io/

桌面版:https://about.draw.io/integrations/#integrations_offline

Whimsical

在线版:https://whimsical.co/

PlantUML

在线版:http://www.plantuml.com/plantuml/

迅捷画图

在线版:https://www.liuchengtu.com/

百度脑图

在线版:http://naotu.baidu.com/

Visual Paradigm Online

在线版:https://online.visual-paradigm.com

Creately

在线版:https://creately.com/

Coggle

在线版:https://coggle.it

3 of 7 in the series: Fltter移动开发相关

理解Flutter widget的生命周期

前言:

生命周期是一个组件加载到卸载的整个周期,熟悉生命周期可以让我们在合适的时机做该做的事情, 
flutter中的State生命周期和android以及React Native的生命周期类似。

生命周期的流程图:

大致可以分为3个阶段:
  • 初始化

  • 状态变化

  • 组件移除

初始化

State初始化时会依次执行 : 构造函数 > initState > didChangeDependencies > Widget build , 此时页面加载完成。
然后我们看一下每个函数的意义:

构造函数

调用次数:1次
这个函数严格意义上来讲不属于生命周期的一部分,因为这个时候State的widget属性为空,无法在构造函数中访问widget的属性 。但是构造函数必然是要第一个调用的。可以在这一部分接收前一个页面传递过来的数据。

initState

Called when this object is inserted into the tree.
调用次数:1次
当插入渲染树的时候调用,这个函数在生命周期中只调用一次。这里可以做一些初始化工作,比如初始化State的变量。

didChangeDependencies

Called when a dependency of this [State] object changes.
初始化时,在initState()之后立刻调用
当依赖的InheritedWidget rebuild,会触发此接口被调用
这个函数会紧跟在initState之后调用,并且可以调用BuildContext.inheritFromWidgetOfExactType,那么BuildContext.inheritFromWidgetOfExactType的使用场景是什么呢?最经典的应用场景是
new DefaultTabController(length: 3, child: new TabBar(
      tabs: [ "主页","订单","我的" ]
      .map( (data)=>new Text(data) ).toList(),
TabBar本来需要定义一个TabController,但是在外面套一层DefaultTabController就不需要定义TabContrller了,看下源码:
@override
  void didChangeDependencies() {
    super.didChangeDependencies();
    _updateTabController();
    _initIndicatorPainter();
  }

void _updateTabController() {
    final TabController newController = widget.controller ?? DefaultTabController.of(context);
    ...
    }
注意到这里DefaultTabController.of(context)
static TabController of(BuildContext context) {
    final _TabControllerScope scope = context.inheritFromWidgetOfExactType(_TabControllerScope);
    return scope?.controller;
  }
实际上就是调用BuildContext.inheritFromWidgetOfExactType,也就说在didChangeDependencies中,可以跨组件拿到数据。

运行时

build

调用次数:多次
初始化之后开始绘制界面,当setState触发的时候会再次被调用

didUpdateWidget

Called whenever the widget configuration changes.
祖先节点rebuild widget时调用 .当组件的状态改变的时候就会调用didUpdateWidget.
理论上setState的时候会调用,但我实际操作的时候发现只是做setState的操作的时候没有调用这个方法。而在我改变代码hot reload时候会调用 didUpdateWidget 并执行 build…
实际上这里flutter框架会创建一个新的Widget,绑定本State,并在这个函数中传递老的Widget。
这个函数一般用于比较新、老Widget,看看哪些属性改变了,并对State做一些调整。
需要注意的是,涉及到controller的变更,需要在这个函数中移除老的controller的监听,并创建新controller的监听。

组件移除

组件移除,例如页面销毁的时候会依次执行:deactivate > dispose

deactivate

Called when this object is removed from the tree.
在dispose之前,会调用这个函数。实测在组件课件状态变化的时候会调用,当组件卸载时也会先一步dispose调用。

dispose

Called when this object is removed from the tree permanently.
调用次数:1次
一旦到这个阶段,组件就要被销毁了,这个函数一般会移除监听,清理环境。

reassemble

hot reload调用
名称
状态
initState
插入渲染树时调用,只调用一次
didChangeDependencies
state依赖的对象发生变化时调用
didUpdateWidget
组件状态改变时候调用,可能会调用多次
build
构建Widget时调用
deactivate
当移除渲染树的时候调用
dispose
组件即将销毁时调用

实际场景

假设我们从A页面跳转到B页面, 那么A,B页面的生命周期会是怎样的呢?
B页面进入初始化状态,依次执行4个函数:构造函数 > initState > didChangeDependencies > Widget build , 此时页面加载完成,进入运行态。
此时A页面依次执行deactivate > build函数。注意 此时A页面并未卸载。
然后我们假设B页面只有一个按钮,点击B页面中的按钮,改变按钮的文字,会执行widget的build方法 ,(理论上也应该执行didUpdateWidget,但我这里没有)。
这时,我们点击返回键从B页面返回到A页面。
A页面重新显示,B页面开始卸载。
那么A先执行deactivate > build , 然后B页面依次执行:deactivate > dispose 。
此时A页面进入运行态,B页面移除。
本次示例B页面代码:
 /*
 * Created by 李卓原 on 2018/9/13.
 * email: zhuoyuan93@gmail.com
 *
 */

import 'package:flutter/material.dart';

class NewsDetailPage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => NewsDetailState();
}

class NewsDetailState extends State<NewsDetailPage> {
  int text = 1;

  NewsDetailState() {
    print('构造函数');
  }

  @override
  void initState() {
    print('init state');
    super.initState();
  }

  @override
  void didChangeDependencies() {
    print('didChangeDependencies');
    super.didChangeDependencies();
  }

  @override
  Widget build(BuildContext context) {
    print('widget build');

    return Scaffold(
      body: Center(
        child: _loading(),
      ),
      appBar: AppBar(
        title: Text('咨询详情'),
      ),
    );
  }

  @override
  void didUpdateWidget(NewsDetailPage oldWidget) {
    print('组件状态改变:didUpdateWidget');
    super.didUpdateWidget(oldWidget);
  }

  @override
  void deactivate() {
    print('移除时:deactivate');
    super.deactivate();
  }

  @override
  void dispose() {
    print('移除时:dispose');
    super.dispose();
  }

  //预加载布局
  Widget _loading() {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        CircularProgressIndicator(
          strokeWidth: 1.0,
        ),
        Container(
          child: Text("正在加载"),
          margin: EdgeInsets.only(top: 10.0),
        )
      ],
    );
  }
}

Tips:

下面内容来自咸鱼技术团队.
当ListView中的item滚动出可显示区域的时候,item会被从树中remove掉,此item子树中所有的state都会被dispose,state记录的数据都会销毁,item滚动回可显示区域时,会重新创建全新的state、element、renderobject
使用hot reload功能时,要特别注意state实例是没有重新创建的,如果该state中存在一下复杂的资源更新需要重新加载才能生效,那么需要在reassemble()添加处理,不然当你使用hot reload时候可能会出现一些意想不到的结果,例如,要将显示本地文件的内容到屏幕上,当你开发过程中,替换了文件中的内容,但是hot reload没有触发重新读取文件内容,页面显示还是原来的旧内容.
idChangeDependencies有两种情况会被调用。
创建时候在initState 之后被调用
在依赖的InheritedWidget发生变化的时候会被调用
正常的退出流程中会执行deactivate然后执行dispose。但是也会出现deactivate以后不执行dispose,直接加入树中的另一个节点的情况。
这里的状态改变包括两种可能:
1.通过setState内容改变
2.父节点的state状态改变,导致孩子节点的同步变化。

App生命周期

需要指出的是如果想要知道App的生命周期,那么需要通过WidgetsBindingObserver的didChangeAppLifecycleState 来获取。通过该接口可以获取是生命周期在AppLifecycleState类中。常用状态包含如下几个:
名称
状态
resumed
可见并能响应用户的输入
inactive
处在并不活动状态,无法处理用户响应
paused
不可见并不能响应用户的输入,但是在后台继续活动中
一个实际场景中的例子:
在不考虑suspending的情况下:
从后台切入前台生命周期变化如下: AppLifecycleState.inactive->AppLifecycleState.resumed;
从前台压后台生命周期变化如下: AppLifecycleState.inactive->AppLifecycleState.paused;

本文主要梳理一下StatefulWidget和StatelessWidget的生命周期

微组件

StatelessWidget

  1. 接收外部数据
  2. 执行部件构造方法
  3. 传入数据改变时会重新渲染UI

StatefulWidget

  1. 接收外部数据
  2. 执行部件构造方法和状态初始化方法
  3. 传入数据和 本类数据改变时都会重新渲染UI
这用到之前写的一个例子01_widget_basic
第一次运行项目

  1. ProductsManager部件初始化
  2. 创建ProductsManagerState
  3. 调用ProductsManagerState中的initState方法
  4. ProductsManagerState渲染
  5. Products部件初始化
  6. Products渲染
    当点击add product按钮数据发生改变时
    1.数据_products发生了变化 通过setState方法通知数据发生了改变 ProductsManagerState build方法被调用
  7. 而Products进行了重新构造,也就是说当外部数据变化时 Products中的 _products 直接被替换成传入的新数据而不是在修改原有数据
flutter拥有类似于react-native的状态机刷新机制,得益于分离出了StatelessWidget和StatefulWidget,资源分配更加合理的了,代码思路也清晰很多.
lutter中的视图Widget像Android中的Activity一样存在生命周期,生命周期的回调函数体都在State中。


组件State的生命周期整理:

创建阶段

Log所示:
image.png

Widget状态改变

操作:横竖屏切换
Log所示:
其他生命周期并没有执行
竖屏切换到横屏执行2次
横屏切换到竖屏执行2次
image.png

App切后台,再切回来

Log所示:
image.png

销毁阶段

Log所示:
image.png
流程如图:
image.png

import 'package:flutter/material.dart';

class LifecycleAppPage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return new _LifecycleAppPageState('构造函数');
  }
}

class _LifecycleAppPageState extends State<LifecycleAppPage>
    with WidgetsBindingObserver {
  String str;

  int count = 0;

  _LifecycleAppPageState(this.str);

  @override
  void initState() {
    print(str);
    print('initState');
    super.initState();
    WidgetsBinding.instance.addObserver(this);
  }

  @override
  void didChangeDependencies() {
    print('didChangeDependencies');
    super.didChangeDependencies();
  }

  @override
  void didUpdateWidget(LifecycleAppPage oldWidget) {
    print('didUpdateWidget');
    super.didUpdateWidget(oldWidget);
  }

  @override
  void deactivate() {
    print('deactivate');
    super.deactivate();
  }

  @override
  void dispose() {
    print('dispose');
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    switch (state) {
      case AppLifecycleState.inactive:
        print('AppLifecycleState.inactive');
        break;
      case AppLifecycleState.paused:
        print('AppLifecycleState.paused');
        break;
      case AppLifecycleState.resumed:
        print('AppLifecycleState.resumed');
        break;
      case AppLifecycleState.suspending:
        print('AppLifecycleState.suspending');
        break;
    }

    super.didChangeAppLifecycleState(state);
  }

  @override
  Widget build(BuildContext context) {
    print('build');
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('lifecycle 学习'),
        centerTitle: true,
      ),
      body: new OrientationBuilder(
        builder: (context, orientation) {
          return new Center(
            child: new Text(
              '当前计数值:$count',
              style: new TextStyle(
                  color: orientation == Orientation.portrait
                      ? Colors.blue
                      : Colors.red),
            ),
          );
        },
      ),
      floatingActionButton: new FloatingActionButton(
          child: new Text('click'),
          onPressed: () {
            count++;
            setState(() {});
          }),
    );
  }
}

class LifecyclePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return new Scaffold(
      body: new LifecycleAppPage(),
    );
  }
}


6 of 7 in the series: Fltter移动开发相关

TextField多行操作


new TextField(
  keyboardType: TextInputType.multiline,
  maxLines: whatever,

Continue reading

2 of 7 in the series: Fltter移动开发相关

sqflite操作相关

SQLite 插件 同时支持 iOS 和Android.
    • 支持事务和批处理
    • 版本开放自动管理
    • 增删改查助手
  • iOS 和 Android的后台线程中执行的DB操作

开始

在您的flutter项目中添加依赖项:
dependencies:
  ...
  sqflite: any

引用示例

Import sqflite.dart
import 'package:sqflite/sqflite.dart';

原始的Sql查询

执行原始SQL查询的示例代码
// Get a location using getDatabasesPath
var databasesPath = await getDatabasesPath();
String path = join(databasesPath, 'demo.db');

// Delete the database
await deleteDatabase(path);

// open the database
Database database = await openDatabase(path, version: 1,
    onCreate: (Database db, int version) async {
  // When creating the db, create the table
  await db.execute(
      'CREATE TABLE Test (id INTEGER PRIMARY KEY, name TEXT, value INTEGER, num REAL)');
});

// Insert some records in a transaction
await database.transaction((txn) async {
  int id1 = await txn.rawInsert(
      'INSERT INTO Test(name, value, num) VALUES("some name", 1234, 456.789)');
  print('inserted1: $id1');
  int id2 = await txn.rawInsert(
      'INSERT INTO Test(name, value, num) VALUES(?, ?, ?)',
      ['another name', 12345678, 3.1416]);
  print('inserted2: $id2');
});

// Update some record
int count = await database.rawUpdate(
    'UPDATE Test SET name = ?, VALUE = ? WHERE name = ?',
    ['updated name', '9876', 'some name']);
print('updated: $count');

// Get the records
List<Map> list = await database.rawQuery('SELECT * FROM Test');
List<Map> expectedList = [
  {'name': 'updated name', 'id': 1, 'value': 9876, 'num': 456.789},
  {'name': 'another name', 'id': 2, 'value': 12345678, 'num': 3.1416}
];
print(list);
print(expectedList);
assert(const DeepCollectionEquality().equals(list, expectedList));

// Count the records
count = Sqflite
    .firstIntValue(await database.rawQuery('SELECT COUNT(*) FROM Test'));
assert(count == 2);

// Delete a record
count = await database
    .rawDelete('DELETE FROM Test WHERE name = ?', ['another name']);
assert(count == 1);

// Close the database
await dat


SqlHelper(助手)

使用助手的示例
inal String tableTodo = 'todo';
final String columnId = '_id';
final String columnTitle = 'title';
final String columnDone = 'done';

class Todo {
  int id;
  String title;
  bool done;

  Map<String, dynamic> toMap() {
    var map = <String, dynamic>{
      columnTitle: title,
      columnDone: done == true ? 1 : 0
    };
    if (id != null) {
      map[columnId] = id;
    }
    return map;
  }

  Todo();

  Todo.fromMap(Map<String, dynamic> map) {
    id = map[columnId];
    title = map[columnTitle];
    done = map[columnDone] == 1;
  }
}

class TodoProvider {
  Database db;

  Future open(String path) async {
    db = await openDatabase(path, version: 1,
        onCreate: (Database db, int version) async {
      await db.execute('''
create table $tableTodo ( 
  $columnId integer primary key autoincrement, 
  $columnTitle text not null,
  $columnDone integer not null)
''');
    });
  }

  Future<Todo> insert(Todo todo) async {
    todo.id = await db.insert(tableTodo, todo.toMap());
    return todo;
  }

  Future<Todo> getTodo(int id) async {
    List<Map> maps = await db.query(tableTodo,
        columns: [columnId, columnDone, columnTitle],
        where: '$columnId = ?',
        whereArgs: [id]);
    if (maps.length > 0) {
      return Todo.fromMap(maps.first);
    }
    return null;
  }

  Future<int> delete(int id) async {
    return await db.delete(tableTodo, where: '$columnId = ?', whereArgs: [id]);
  }

  Future<int> update(Todo todo) async {
    return await db.update(tableTodo, todo.toMap(),
        where: '$columnId = ?', whereArgs: [todo.id]);
  }

  Future close() async => db.close();
}

Transaction

不要使用数据库,而只在事务中使用Transaction对象来访问数据库
await database.transaction((txn) async {
  // Ok
  await txn.execute('CREATE TABLE Test1 (id INTEGER PRIMARY KEY)');
  
  // DON'T  use the database object in a transaction
  // this will deadlock!
  await database.execute('CREATE TABLE Test2 (id INTEGER PRIMARY KEY)');
});

批处理支持

为了避免频繁的交互,可以使用Batch:
batch = db.batch();
batch.insert('Test', {'name': 'item'});
batch.update('Test', {'name': 'new_item'}, where: 'name = ?', whereArgs: ['item']);
batch.delete('Test', where: 'name = ?', whereArgs: ['item']);
results = await batch.commit();
获取每个操作的结果都会带来成本(插入的id和更新和删除的更改的数量),尤其是在执行额外SQL请求的Android上。如果不关心结果并担心消耗大量资源和性能,可以使用
await batch.commit(noResult: true);
警告,在事务期间,在提交事务之前不会提交批处理
wait database.transaction((txn) async {
  var batch = txn.batch();
  
  // ...
  
  // commit but the actual commit will happen when the transaction is commited
  // however the data is available in this transaction
  await batch.commit();
  
  //  ...
});
默认情况下,一遇到错误(通常恢复未提交的更改),批处理就停止。您可以忽略错误,即使碰到一个操作失败,也能运行和提交操作成功每个提交:
await batch.commit(continueOnError: true);

表和列名

一般来说,最好避免对实体名称使用SQLite关键字。如果使用下列名称中的任何一个:
"add","all","alter","and","as","autoincrement","between","case","check",
"collate","commit","constraint","create","default","deferrable","delete",
"distinct","drop","else","escape","except","exists","foreign","from",
"group","having","if","in","index","insert","intersect","into","is",
"isnull","join","limit","not","notnull","null","on","or","order",
"primary","references","select","set","table","then","to",
"transaction","union","unique","update","using","values","when","where"

助手会逃避这个名字,如。。
db.query('table')
以上这行代码将等同于手动在表名周围添加双引号(令人困惑的是,这里命名的表),等同于如下代码
db.rawQuery('SELECT * FROM "table"');
但是在任何其他原始语句(包括order.、where、group.)中,确保使用双引号正确地转义名称。例如,参见下面,列名组在列参数中没有转义,而是在where参数中转义
db.query('table', columns: ['group'], where: '"group" = ?', whereArgs: ['my_group']);

SQLite 支持的数据类型

Sqlite还没有对值进行有效性检查,因此请避免不支持的类型

DateTime

SQLite不支持DateTime类型。我个人将它们存储为int(.sSinceEpoch)或string(iso8601)

Bool

SQLite不支持bool类型。使用整数和0和1值。

INTEGER

    • Dart type: int
  • 取值范围: 从-2^63 到 2^63 – 1

REAL

  • Dart type: num

TEXT

  • Dart type: String

BLOB

    • Dart type: Uint8List
  • Dart type List<int> 是支持的,但是不推荐使用 慢速转换)

当前问题

    • Due to the way transaction works in SQLite (threads), concurrent read and write transaction are not supported. All calls are currently synchronized and transactions block are exclusive. I thought that a basic way to support concurrent access is to open a database multiple times but it only works on iOS as Android reuses the same database object. I also thought a native thread could be a potential future solution however on android accessing the database in another thread is blocked while in a transaction…
  • Currently INTEGER are limited to -2^63 to 2^63 – 1 (although Android supports bigger ones)
上述机翻
由于事务在SQLIT(线程)中的工作方式,不支持并发读写事务。所有调用当前都是同步的,事务块是独占的。我认为支持并发访问的基本方法是多次打开数据库,但是它只能在iOS上工作,因为Android重用了相同的数据库对象。我还认为本机线程可能是未来可能的解决方案,但是当android访问另一个线程中的数据库时,在事务中会阻塞……
目前INTEGER被限制在-2^63到2^63-1(尽管Android支持更大的)

更多

相关类库


by phoenix翻译,原文转自

1 of 7 in the series: Fltter移动开发相关

实体类自动生成工具的配置

1.实体类自动生成

a.在线实体类处自动生成

1).JsonToDartClass转换地址-1:

2).JsonToDartClass转换地址-2:

b.离线实体类自动生成工具

Formatter是开源的,项目地址;https://github.com/debuggerx01/JSONFormat4Flutter

2.使用工程自动生成代码

参考资料

操作步骤

    1. 工程依赖中加入依赖库
      dependencies: 
        cupertino_icons: ^0.1.2 
        fluttertoast: ^2.0.3
        json_annotation: ^1.2.0
      dev_dependencies:
        build_runner: ^0.10.1+1
        json_serializable: ^1.1.0
      
    1. 建立需要生成的实体类
      import 'package:json_annotation/json_annotation.dart';
      
      part 'User.g.dart';
      
      @JsonSerializable()
      class User {
        User(this.id,
            this.username,
           ...
       );
       
        String username; 
      。。。
        factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
      
        Map<String, dynamic> toJson() => _$UserToJson(this);
      
        // 命名构造函数
        User.empty();
      
      }
      
  1. 在控制台输入生成命令
    flutter packages pub run build_runner build
    

5 of 7 in the series: Fltter移动开发相关

Flutter环境更新方法

常规更新方法:

运行:flutter upgrade,然后等待自动更新完成即可,

非常贵更新方法:

1.更新flutter代码库

找到flutter库的安装目录,运行git,同步拉取到最新的代码,

2.更新依赖

运行flutter doctor -v 自动下载更新依赖的库
Microsoft Windows [版本 10.0.17134.112]
(c) 2018 Microsoft Corporation。保留所有权利。

F:\JavaWorker\deheng\android\hemodialysis>flutter doctor -v
Checking Dart SDK version...
Downloading Dart SDK from Flutter engine 72c7a7567228cdaf8b7aa4a9e3d212ef9d4cc0ed...
Unzipping Dart SDK...
Building flutter tool...
Running pub upgrade...
Downloading package sky_engine...                            1.1s
Downloading common tools...                                  2.6s
Downloading windows-x64 tools...                             5.9s
Downloading android-arm-profile/windows-x64 tools...         1.2s
Downloading android-arm-release/windows-x64 tools...         0.8s
Downloading android-arm64-profile/windows-x64 tools...       0.9s
Downloading android-arm64-release/windows-x64 tools...       0.9s
Downloading android-x86 tools...                             4.8s
Downloading android-x64 tools...                             4.9s
Downloading android-arm tools...                             3.4s
Downloading android-arm-profile tools...                     1.8s
Downloading android-arm-release tools...                     2.2s
Downloading android-arm64 tools...                           1.9s
Downloading android-arm64-profile tools...                   1.8s
Downloading android-arm64-release tools...                   1.5s
Downloading android-arm-dynamic-profile tools...             1.9s
Downloading android-arm-dynamic-release tools...             2.8s
Downloading android-arm64-dynamic-profile tools...           2.0s
Downloading android-arm64-dynamic-release tools...           1.7s
[√] Flutter (Channel beta, v0.10.3-pre.3, on Microsoft Windows [Version 10.0.17134.112], locale zh-CN)
    • Flutter version 0.10.3-pre.3 at F:\ProgramFiles\flutter
    • Framework revision 233435c02a (42 minutes ago), 2018-11-30 10:00:27 +0800
    • Engine revision 72c7a75672
    • Dart version 2.1.0 (build 2.1.0-dev.9.4 f9ebf21297)

[√] Android toolchain - develop for Android devices (Android SDK 28.0.3)
    • Android SDK at F:\ProgramFiles\android\sdk
    • Android NDK location not configured (optional; useful for native profiling support)
    • Platform android-28, build-tools 28.0.3
    • ANDROID_HOME = F:\ProgramFiles\android\sdk
    • Java binary at: F:\ProgramFiles\android\Android Studio\jre\bin\java
    • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1136-b06)
    • All Android licenses accepted.

[√] Android Studio (version 3.2)
    • Android Studio at F:\ProgramFiles\android\Android Studio
    • Flutter plugin version 31.1.1
    • Dart plugin version 181.5656
    • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1136-b06)

[√] IntelliJ IDEA Ultimate Edition (version 2018.2)
    • IntelliJ at F:\ProgramFiles\JetBrains\IntelliJ IDEA 2018.2.4
    • Flutter plugin version 30.0.2
    • Dart plugin version 182.5124

[√] Connected device (2 available)
    • MI PAD 4  • 6074f4a0     • android-arm64 • Android 8.1.0 (API 27)
    • Che1 CL20 • f4e3fb7f933b • android-arm   • Android 4.4.4 (API 19)

• No issues found!

F:\JavaWorker\deheng\android\hemodialysis>

4 of 7 in the series: Fltter移动开发相关