2022年6月30日星期四

ListView.builder() 渲染長列表數據 | 實例:無限加載列表

 

ListView.builder() 渲染長列表數據

下面是ListView.builder() 的註釋如下:

  /// This constructor is appropriate for list views with a large (or infinite)
  /// number of children because the builder is called only for those children
  /// that are actually visible.

直接使用 ListView(children: List<Widget>) 的優點是簡單邊界,直接生成一個 List<Widget> 的列表,然後賦值給 ListView 的 children 參數即可

缺點是整個ListView 是沒有回收機制的,會一次性創建出所有的子項,構建完成整個ListView。

這意味著一旦列表長度非常長,比如無限滾動列表的場景中,列表長度非常長,直接創建ListView 的所有子項可能會有內存問題,過高的內存會引起Crash

------------------------------------------------------------------------------------------

import 'dart:math';

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(

primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}

class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);


final String title;

@override
State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

var rng = Random();
static const loadingTag = "##loading##"; //表尾标记
var _words = <String>[loadingTag];

@override
Widget build(BuildContext context) {

return Scaffold(
appBar: AppBar(

title: Text(widget.title),
),
body: Center(

child: Scrollbar(
child: ListView.separated(
itemCount: _words.length,
itemBuilder: (context, index) {
//如果到了表尾
if (_words[index] == loadingTag) {
//不足100条,继续获取数据
if (_words.length - 1 < 100) {
//获取数据
_retrieveData();
//加载时显示loading
return Container(
padding: const EdgeInsets.all(16.0),
alignment: Alignment.center,
child: SizedBox(
width: 24.0,
height: 24.0,
child: CircularProgressIndicator(strokeWidth: 2.0),
),
);
} else {
//已经加载了100条数据,不再获取数据。
return Container(
alignment: Alignment.center,
padding: EdgeInsets.all(16.0),
child: Text(
"没有更多了",
style: TextStyle(color: Colors.grey),
),
);
}
}
//显示单词列表项
return ListTile(title: Text(index.toString()+':'+_words[index]));
},
separatorBuilder: (context, index) => Divider(height: .0),
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: (){

},
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}

void _retrieveData() {
Future.delayed(Duration(seconds: 2)).then((e) {
setState(() {

_words.insertAll(
_words.length - 1,
//每次生成20random number
[for(var i=0;i<20;i++) rng.nextInt(100).toString() ] ,
);
});
});
}
}

ListView Scrollbar

 Scrollbar(

    child: ListView.builder(
      itemCount: 50,
      itemBuilder: (context, index) => ListTile(title: Text("Item= ${index + 1}"),),),
)

2022年6月29日星期三

wait until poped back (when use Navigator push)

onPressed: () async {

  var value = await Navigator.push(

    context,

    MaterialPageRoute(builder: (context) => const SecondPage()),

  );

  print('backed');


} 

check app lifecycle paused or resumed state

 hi the answer is to use WidgetsBindingObserver. in your app just add

class _MyScreenState extends State<MyScreen> with WidgetsBindingObserver{}


add the following inside our initState:


WidgetsBinding.instance.addObserver(this);


didChangeAppLifecycleState will be:



@override
     void didChangeAppLifecycleState(AppLifecycleState state) {
     super.didChangeAppLifecycleState(state);
      if (state == AppLifecycleState.paused) {
       // went to Background
         }
       if (state == AppLifecycleState.resumed) {
       // came back to Foreground
       }
    }

initState

put it in State:


 @override

void initState() {
super.initState();

}

2022年6月28日星期二

indexedStack內控制indexedStack外的widget (e.g. in stack to nav push a page out of stack)

! use provider to save the context 

let NavBar is the widget out of the indexedStack

-create NavBarVM and let it be a provider

-create a variable "navBarContext" in NavBarVM

-create the function "setNavBarContext" & "getNavBarContext"


-when navbar build:

build(BuildContext context) {

setNavBarContext(context);

}

something like that


-in the indexedStack widget

BuildContext navBarContext = context.read<NavbarVM>().getNavBarContext();

Navigator.push(

  navBarContext,

  MaterialPageRoute(

      builder: (navBarContext) =>

          SecondPage()


  ),

);

zsh: command not found: flutter

 

  1. Open terminal.

  2. vim $HOME/.zshrc

  3. Press "i" key for going to insert mode.

  4. add the following line in the opened file:

    export PATH="$PATH:/$HOME/YOUR_FLUTTER_DIR/flutter/bin"

  5. Press "Esc" then write :wq! in terminal and press enter to exit vim.

  6. Reopen the terminal and check "flutter doctor"

Flutter how to share image on assets folder?

 First get your image as bytes and copy to temp file.

      final bytes = await rootBundle.load('assets/image.jpg');
      final list = bytes.buffer.asUint8List();

      final tempDir = await getTemporaryDirectory();
      final file = await File('${tempDir.path}/image.jpg').create();
      file.writeAsBytesSync(list);


Then use share package to share it, should work;



Share.shareFiles(['${file.path}'], text: 'Great picture');

2022年6月26日星期日

scrollable ListView

 ListView(

  shrinkWrap: true,

  padding: const EdgeInsets.all(20.0),

  physics: const AlwaysScrollableScrollPhysics(),

  children: report.data.map((e) {

    return Text(

      DateFormat.yMd().format(e.bmi.createdAt),

      style: FlutterFlowTheme.of(context)

          .bodyText1

          .override(

        fontFamily: 'Montserrat',

        color: Color(0xFF0696B9),

        fontSize: 18,

        fontWeight: FontWeight.w500,

      ),

    );

  }).toList(),

)

Format DateTime in Flutter + Cheatsheet

 Step 1: Add the intl package.

dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2
intl: ^0.17.0 # <-- SEE HERE

Step 2: Get the date that you want to format.

DateTime now = DateTime.now();

Step 3: Use the named constructor and then call the format() method and then provide the date. 

String formattedDate = DateFormat.yMMMEd().format(now);




Custom DateFormat

DateFormat('yyyy-MM-dd hh:mm')

The following characters are available in explicit patterns:

Symbol   Meaning                Presentation       Example
------   -------                ------------       -------
G        era designator         (Text)             AD
y        year                   (Number)           1996
M        month in year          (Text & Number)    July & 07
L        standalone month       (Text & Number)    July & 07
d        day in month           (Number)           10
c        standalone day         (Number)           10
h        hour in am/pm (1~12)   (Number)           12
H        hour in day (0~23)     (Number)           0
m        minute in hour         (Number)           30
s        second in minute       (Number)           55
S        fractional second      (Number)           978
E        day of week            (Text)             Tuesday
D        day in year            (Number)           189
a        am/pm marker           (Text)             PM
k        hour in day (1~24)     (Number)           24
K        hour in am/pm (0~11)   (Number)           0
Q        quarter                (Text)             Q3
'        escape for text        (Delimiter)        'Date='
''       single quote           (Literal)          'o''clock'