Learn More →
??
if value of the left is null
than return the vlaue from the right
EX: A ?? false
if A is null than return false
final
A value that can be assigned only once
Container(
height: 100,
width: 100,
child: ListWheelScrollView.useDelegate(
physics: FixedExtentScrollPhysics(),
onSelectedItemChanged: (index) {
// set value here
},
itemExtent: 50,
childDelegate: ListWheelChildBuilderDelegate(
builder: (current, index) {
return index >= 0 && index <= 50
? Center(
child: Text("$index"),
)
: null;
}
)
)
)
List.filled( <how many length> , <value to fill in> )
to fill the length of value to the listStatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
bool isLightOn = false; // Local state for the switch
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
isLightOn ? "Light is ON" : "Light is OFF",
style: TextStyle(fontSize: 20),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () {
setState(() {
isLightOn = !isLightOn; // Toggle the state
});
},
child: Text("Toggle Light"),
),
],
);
},
)
Container(
color: Colors.greenAccent,
height: 100,
width: 100,
child: ListWheelScrollView.useDelegate(
itemExtent: 50,
perspective: 0.005,
diameterRatio: 1.5,
physics: FixedExtentScrollPhysics(),
onSelectedItemChanged: (index) {
setState(() {
// selectedMinutes = index;
});
},
childDelegate: ListWheelChildBuilderDelegate(
builder: (context, index) {
if (index >= 0 && index <= 60) {
return Center(
child: Text(
index.toString().padLeft(2, '0'),
style: TextStyle(fontSize: 25),
),
);
}
return null;
},
childCount: 61,
),
)
)
tick
in windows form, run a task base on a fixed timeCreate a value for storing countdown time, a late init variable with type Timer
for storing Timer and a variable for checking a timer is running
var _time = 60;
late Timer timer;
var timerStarted = false;
Start a Timer
void start() {
if (!timerStarted)
timerStarted = true;
else
return;
timer = Timer.periodic(Duration(seconds: 1), (time) {
setState(() {
if (_time > 0) {
_time--;
} else {
timer.cancel();
timerStarted = false;
}
});
});
}
Pause the timer
void pause(){
timer.cancel();
timerStarted = false;
}
CustomPainter
paint
function contain the main logic of drawingshouldRepaint
function is for setting whether the paint should be update return true
false
or a logicclass Artist extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.red
..strokeWidth = 10
..style = PaintingStyle.stroke;
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
Padding(
padding: const EdgeInsets.only(left: 10, bottom: 10),
child: Placeholder()
)
BoxDecoration
Container(
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(10.0),
),
child: Placeholder(),
)
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20.0),
topRight: Radius.circular(15.0),
bottomLeft: Radius.circular(10.0),
bottomRight: Radius.circular(5.0),
)
ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Image(
image: NetworkImage("https://eliaschen.dev/eliaschen.jpg"),
),
)
BoxDecoration
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 1,
blurRadius: 10,
offset: Offset(0, 5),
)
]
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Colors.black12,
offset: Offset(0.5, 0.5),
blurRadius: 5,
spreadRadius: 0.5,
),
BoxShadow(
color: Colors.white,
offset: const Offset(0.0, 0.0),
blurRadius: 0.0,
spreadRadius: 0.0,
),
],
),
child: Padding(
padding: const EdgeInsets.all(20),
child: Text("data"),
),
)
Row,Flex
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: WidgetStateProperty.all(Colors.grey),
foregroundColor: WidgetStateProperty.all(Colors.black),
shape: WidgetStateProperty.all(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)
)
)
)
)
TweenAnimationBuilder(
tween: Tween(
begin: 1.0,
end: originalTime > 0
? currentTime / originalTime
: 0.0),
duration: Duration(seconds: 1),
builder: (BuildContext context, double value,
Widget? child) {
return CircularProgressIndicator(
value: value,
strokeWidth: 10,
);
},
)
SizedBox(
width: double.infinity,
height: 500,
child: ReorderableListView(
onReorder: (int oldIndex, int newIndex) {
setState(() {
if (newIndex > oldIndex) {
newIndex -= 1;
}
final item = data.removeAt(oldIndex);
data.insert(newIndex, item);
});
},
children: data.map((item) {
return ListTile(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
key: ValueKey(item['id']),
title: Text(item['name']),
subtitle: Text(item['type'].toString().toUpperCase()),
leading: Text(TimeFormatter(item['time'])),
trailing: const Icon(Icons.drag_indicator),
);
}).toList(),
),
)
Remember to put the DraggableScrollableSheet
into a Stack
widget to let it float on top other widget
DraggableScrollableSheet(
initialChildSize: 0.15,
minChildSize: 0.15,
maxChildSize: 1,
builder: (context, scroller) {
return Container(
decoration: const BoxDecoration(
color: Colors.white,
),
child: SingleChildScrollView(
controller: scroller,
),
);
},
);
var workType = TextEditingController();
DropdownMenu(
label: Text("Chose the work type"),
width: 200,
initialSelection: 0,
controller: workType,
dropdownMenuEntries: [
DropdownMenuEntry(value: 0, label: "Rest"),
DropdownMenuEntry(value: 1, label: "Work"),
]
)
use a textController to make the inputfield auto update the value in the input box when the value change
<controller-name>.text
to get the valuevar email = TextEditingController();
TextField(
controller: password,
obscureText: hidePassword,
obscuringCharacter: '*',
decoration: InputDecoration(
labelText: "主密碼",
suffixIcon: IconButton(
icon: Icon(
hidePassword ? Icons.visibility : Icons.visibility_off,
),
onPressed: () {
setState(() {
hidePassword = !hidePassword;
});
},
),
),
)
PopupMenuButton(
itemBuilder: (BuildContext context) => <PopupMenuEntry>[
const PopupMenuItem(
child: Text('Option1'),
),
const PopupMenuItem(
child: Text('Option2'),
),
const PopupMenuItem(
child: Text('Option3'),
),
],
)
ScaffoldMessenger.of(context)
.showSnackBar(
SnackBar(
duration: Duration(seconds: 2),
content: Text("登入成功")
)
);
Dismissible(
key: ValueKey(<a-string-that-is-static>),
onDissmissed: (direction){
if(direction == DismissDirection.endToStart){
// when swipe right to left
}else if(direction == DismissDirection.startToEnd){
// when swipe left to right
}else if(direction == DismissDirection.horizontal){
// when swipe both direction
}
}
)
Clipboard.setData(ClipboardData(text: password))
Scaffold(
appBar: AppBar(
title: Text('Playground'),
actions: [
// Right side
],
),
drawer: Drawer(
child: Placeholder(),
),
floatingActionButton: FloatingActionButton(
onPressed: () {},
child: Icon(Icons.add),
),
body: Placeholder(),
)
PopupMenuButton(
itemBuilder: (context) => [
PopupMenuItem(
onTap: () {},
child: Placeholder()
),
PopupMenuItem(
onTap: () {},
child: Placeholder()
),
]
)
All the dialog use pop back to dismiss
ElevatedButton(
onPressed: () => showDialog(
context: context,
builder: (context) => Dialog(
child: Column(
children: [
Text("Hello Dialog"),
],
),
)),
child: Text("Show Dialog")
)
ElevatedButton(
onPressed: () => showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text("AlertDialog"),
content: Text(
"hello there",
),
actions: [
TextButton(
onPressed: () {}, child: Text("Close")),
TextButton(onPressed: () {}, child: Text("Ok"))
],
),
),
child: Text("Show AlertDialog")
),
ElevatedButton(
onPressed: () => showDialog(
context: context,
builder: (BuildContext context) => Dialog.fullscreen(
child: Column(
children: [
Text("Hello FullScreen Dialog"),
],
),
)),
child: Text("Show FullScreen Dialog"),
)
Navigator.push(context,
MaterialPageRoute(builder: (context) => YourDestinationScreen())
);
Navigator.pop(context);
await Future.delayed(const Duration(seconds: 2));
Duration
support unit days
horus
minutes
seconds
milliseconds
microseconds
MainActivity.kt
create a methodChannel to retrive event in flutter layer
package dev.eliaschen.re_flutter_skills_pre
import android.hardware.*
import android.os.Bundle
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import kotlin.math.sqrt
class MainActivity : FlutterActivity(), SensorEventListener {
private lateinit var sensorManager: SensorManager
private var methodChannel: MethodChannel? = null
private var lastShakeTime = 0L
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
methodChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "shake_detector")
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
sensorManager = getSystemService(SENSOR_SERVICE) as SensorManager
sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)?.also {
sensorManager.registerListener(this, it, SensorManager.SENSOR_DELAY_UI)
}
}
override fun onSensorChanged(event: SensorEvent?) {
event?.values?.let {
if (sqrt((it[0] * it[0] + it[1] * it[1] + it[2] * it[2]).toDouble()) / 9.81 > 2.7) {
val now = System.currentTimeMillis()
if (now - lastShakeTime > 500) {
lastShakeTime = now
methodChannel?.invokeMethod("onShake", null)
}
}
}
}
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {}
}
ShakeDetector.startListening();
to excute a function when the phone shake
import 'package:flutter/services.dart';
class ShakeDetector {
static const platform = MethodChannel('shake_detector');
static void startListening(Function onShake) {
platform.setMethodCallHandler((call) async {
if (call.method == "onShake") {
onShake();
}
});
}
}
@override
void initState() {
super.initState();
ShakeDetector.startListening(() {
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text("the phone shake")));
});
}
.now()
get the current time.parse()
parsing from ISO 8601.fromMillisecondsSinceEpoch()
parsing from UNIX TimeStampDateTime fromISO = DateTime.parse(<iso-time-string>);
DateTime fromTimeStamp = DateTime.parse(<UNIX-TimeStamp>);
Future<void> fetchData() async{
final url = Uri.parse('<API-endpoint>');
final httpClient = HttpClient();
try {
final request = await httpClient.getUrl(url);
final response = await request.close(); // Send the request
if (response.statusCode == HttpStatus.ok) {
final res = await response.transform(utf8.decoder).join();
final jsonData = jsondecode(res);
/* handle return */
}
} catch (e) {
/* handle error */
} finally {
httpClient.close();
}
}
request.headers.add()
to add header to the requestrequest.headers.add("search","na");
Create a Map for storing headers
Map<String, dynamic> body = {
'title': 'Flutter POST Request',
'body': 'This is a test post',
'userId': 1,
};
Then add body to the request
request.add(utf8.encode(jsonEncode(body)));
basically same as the GET request, just replace httpClient.getUrl(url)
to httpClient.postUrl(url)
[ ]
instead of .
in Javascriptassets/data.json
{
"result": {
"offset": 0,
"limit": 10000,
"count": 1,
"sort": "",
"results": [
{
"_id": 1,
"Station": "後山埤站",
"Destination": "頂埔站",
"UpdateTime": "2015-08-24T12:03:27.907"
},
{
"_id": 2,
"Station": "永寧站",
"Destination": "南港展覽館站",
"UpdateTime": "2015-08-24T12:03:27.907"
},
{
"_id": 3,
"Station": "台北車站",
"Destination": "南港展覽館站",
"UpdateTime": "2015-08-24T12:03:37.5"
}
]
}
}
assets
String jsonString = await rootBundle.loadString('assets/data.json');
final List<dynamic> jsonResponse = jsonDecode(jsonString)["results"][""];
app dir
final directory = await getApplicationDocumentsDirectory();
final filePath = '${directory.path}/$filename';
final file = File(filePath);
final jsonString = await file.readAsString();
final List<dynamic> jsonData = jsonDecode(jsonString);
Create class for data parsing & typesafe
class StationSchema {
final int id;
final String Station;
final String Destination;
final String UpdateTime;
StationSchema({
required this.id,
required this.Station,
required this.Destination,
required this.UpdateTime,
});
factory StationSchema.fromMap(Map<String, dynamic> map) {
return StationSchema(
id: map['_id'],
Station: map['Station'],
Destination: map['Destination'],
UpdateTime: map['UpdateTime'],
);
}
}
Use fromMap
to convert Map format to the stander list in dart
Future<List<StationSchema>> parseJson() async {
String jsonString = await rootBundle.loadString('assets/data.json');
List<dynamic> jsonMap = jsonDecode(jsonString)[0]['result']['results'];
List<StationSchema> stations = jsonMap.map((json) => StationSchema.fromMap(json)).toList();
await Future.delayed(Duration(seconds: 2));
return stations;
}
Future Builder
snapshot.data
to get the data of the list we do json parsing beforeFutureBuilder<List<StationSchema>>(
key: ValueKey<int>(key),
future: parseJson(),
builder: (context, snapshot) {
// something like ListView in there
},
)
sqflite
path_provider
class TodoSchema {
final String todo;
final int done;
final int? id;
TodoSchema({this.id, required this.todo, required this.done});
Map<String, dynamic> toMap() {
return {'id': id, 'todo': todo, 'done': done};
}
}
rawQuery |
excute |
---|---|
return Future<List<Map<String, dynamic>>> |
Don’t have a retrun value |
require await |
require await |
can be save to List<Map<String, dynamic>> |
use ? to set the value later in [] |
List<Map<String,dynamic>> data = await <db>.rawQuery('<sql-statement>', [ ]) |
await <db>.excute('<sql-statement>', [ ]) |
class Appdb {
static Database? db;
static Future<Database> getDbConnection() async {
db ??= await initDatabase(); // if db is null, initialize it
return db!; // return db if it is not null
}
static Future<List<TodoSchema>> getTodos() async {
final Database db = await getDbConnection();
final List<Map<String, dynamic>> maps = await db
.query('todos ORDER BY id DESC'); // get all todos in descending order
return List.generate(maps.length, (index) {
return TodoSchema(
id: maps[index]['id'],
todo: maps[index]['todo'],
done: maps[index]['done'],
);
});
}
static Future<void> addTodo(TodoSchema todo) async {
final Database db = await getDbConnection();
await db.insert(
'todos',
todo.toMap(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
static Future<void> deleteTodo(int id) async {
final Database db = await getDbConnection();
await db.delete(
'todos',
where: 'id = ?',
whereArgs: [id],
);
}
static Future<void> deleteAllTodos() async {
final Database db = await getDbConnection();
await db.delete('todos');
}
static Future<void> updateTodo(int id, int done) async {
final Database db = await getDbConnection();
await db.update(
'todos',
{'done': done},
where: 'id = ?',
whereArgs: [id],
);
}
static Future<Database> initDatabase() async {
var databasesPath = await getApplicationDocumentsDirectory(); // IMPORTANT
String path = join(databasesPath.path, 'todoapp.db');
return await openDatabase(path, version: 1,
onCreate: (Database db, int version) async {
await db.execute(
'CREATE TABLE todos(id INTEGER PRIMARY KEY, todo TEXT, done INTEGER)');
});
}
}
A jetpack compos note written by EliasChen
Jun 3, 2025Tip/Shortcut Description Parameter Info Ctrl + P to view parameters for a method/constructor Type Info Ctrl + Shift + P to check the type of a variable Show Intention Actions Alt + Enter to see suggestions and quick fixes Code Completion Ctrl + Space to see available options within an XML tag or on a view Extract Method Ctrl + Alt + M to generate a method from selected code Surround With Ctrl + Alt + T to surround code with if statements or try-catch blocks Precise Selection Hold Shift and use left/right arrow keys to select characters; add Ctrl for whole words Move Lines Ctrl + Shift + Up/Down arrows to move lines of code Refactor/Rename Shift + F6 to rename variables, methods, or classes Switch Between Tabs Alt + Left/Right arrows to navigate between open tabs Navigate Methods and Classes Alt + Up/Down arrows to jump between different methods and classes Select by Indentation Use the middle mouse button to select code by indentation Reformat Code Ctrl + Alt + L to reformat and properly indent your code Comment Block Ctrl + Shift + / to comment or uncomment a block of code Show Errors and Warnings Alt + 6 to display all errors and warnings in your project Navigate to Last Edit Location Ctrl + Shift + Backspace to return to the last place you edited Navigate Between Errors F2 to jump between errors in your code Customize Keymap Go to File > Settings > Keymap to customize or create your own key shortcuts Macros Record and replay actions via Edit > Macros > Start Macro Recording. Assign a shortcut in Keymap settings
Mar 19, 2025Kotlin_Mascot
Mar 12, 2025or
By clicking below, you agree to our terms of service.
New to HackMD? Sign up