@el-j/magic-helix-plugins 4.0.0-beta.6 → 4.0.0-beta.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{BasePlugin-6wv0hYJ9.js → BasePlugin-BXz3Zpy5.js} +33 -16
- package/dist/BasePlugin-Be6rLq9o.cjs +1 -0
- package/dist/cpp/index.cjs +3 -3
- package/dist/cpp/index.mjs +2 -2
- package/dist/csharp/index.cjs +1 -1
- package/dist/csharp/index.mjs +11 -10
- package/dist/dart/templates/flutter.md +340 -0
- package/dist/dart/templates/lang-dart.md +233 -0
- package/dist/elixir/templates/lang-elixir.md +147 -0
- package/dist/go/index.cjs +3 -3
- package/dist/go/index.mjs +38 -23
- package/dist/index.cjs +147 -1
- package/dist/index.mjs +581 -22
- package/dist/java/index.cjs +2 -2
- package/dist/java/index.mjs +4 -3
- package/dist/nodejs/index.cjs +2 -2
- package/dist/nodejs/index.mjs +60 -29
- package/dist/php/index.cjs +2 -2
- package/dist/php/index.mjs +7 -6
- package/dist/python/index.cjs +4 -4
- package/dist/python/index.mjs +57 -31
- package/dist/ruby/index.cjs +2 -2
- package/dist/ruby/index.mjs +6 -5
- package/dist/rust/index.cjs +3 -3
- package/dist/rust/index.mjs +7 -6
- package/dist/swift/index.cjs +2 -2
- package/dist/swift/index.mjs +4 -3
- package/package.json +1 -1
- package/dist/BasePlugin-odQJAKA-.cjs +0 -1
|
@@ -1,21 +1,22 @@
|
|
|
1
|
-
import * as
|
|
2
|
-
import * as
|
|
3
|
-
|
|
1
|
+
import * as n from "node:fs";
|
|
2
|
+
import * as s from "node:path";
|
|
3
|
+
import { fileURLToPath as c } from "node:url";
|
|
4
|
+
class u {
|
|
4
5
|
// Helper methods for common operations
|
|
5
6
|
/**
|
|
6
7
|
* Check if a file exists in the project
|
|
7
8
|
*/
|
|
8
9
|
fileExists(e, r) {
|
|
9
|
-
return
|
|
10
|
+
return n.existsSync(s.join(e, r));
|
|
10
11
|
}
|
|
11
12
|
/**
|
|
12
13
|
* Read a file from the project
|
|
13
14
|
*/
|
|
14
15
|
readFile(e, r) {
|
|
15
16
|
try {
|
|
16
|
-
const t =
|
|
17
|
-
if (
|
|
18
|
-
return
|
|
17
|
+
const t = s.join(e, r);
|
|
18
|
+
if (n.existsSync(t))
|
|
19
|
+
return n.readFileSync(t, "utf-8");
|
|
19
20
|
} catch {
|
|
20
21
|
}
|
|
21
22
|
return null;
|
|
@@ -36,15 +37,31 @@ class l {
|
|
|
36
37
|
* Get project name from path
|
|
37
38
|
*/
|
|
38
39
|
getProjectName(e) {
|
|
39
|
-
return
|
|
40
|
+
return s.basename(e);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* List entries in a project directory (non-recursive)
|
|
44
|
+
*/
|
|
45
|
+
listFiles(e) {
|
|
46
|
+
try {
|
|
47
|
+
return n.readdirSync(e);
|
|
48
|
+
} catch {
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Get the directory name from import.meta.url (ES module alternative to __dirname)
|
|
54
|
+
*/
|
|
55
|
+
getDirname(e) {
|
|
56
|
+
return s.dirname(c(e));
|
|
40
57
|
}
|
|
41
58
|
/**
|
|
42
59
|
* Load template from file
|
|
43
60
|
*/
|
|
44
61
|
async loadTemplateFromFile(e) {
|
|
45
62
|
try {
|
|
46
|
-
if (
|
|
47
|
-
return
|
|
63
|
+
if (n.existsSync(e))
|
|
64
|
+
return n.readFileSync(e, "utf-8");
|
|
48
65
|
} catch {
|
|
49
66
|
}
|
|
50
67
|
return null;
|
|
@@ -52,12 +69,12 @@ class l {
|
|
|
52
69
|
/**
|
|
53
70
|
* Create a template definition with lazy loading
|
|
54
71
|
*/
|
|
55
|
-
createTemplate(e, r, t,
|
|
72
|
+
createTemplate(e, r, t, i = {}) {
|
|
56
73
|
return {
|
|
57
74
|
name: e,
|
|
58
75
|
tags: r,
|
|
59
76
|
content: t,
|
|
60
|
-
...
|
|
77
|
+
...i
|
|
61
78
|
};
|
|
62
79
|
}
|
|
63
80
|
/**
|
|
@@ -65,9 +82,9 @@ class l {
|
|
|
65
82
|
*/
|
|
66
83
|
parseDependencies(e, ...r) {
|
|
67
84
|
const t = {};
|
|
68
|
-
for (const
|
|
69
|
-
const
|
|
70
|
-
|
|
85
|
+
for (const i of r) {
|
|
86
|
+
const a = e[i];
|
|
87
|
+
a && typeof a == "object" && Object.assign(t, a);
|
|
71
88
|
}
|
|
72
89
|
return t;
|
|
73
90
|
}
|
|
@@ -93,5 +110,5 @@ class l {
|
|
|
93
110
|
}
|
|
94
111
|
}
|
|
95
112
|
export {
|
|
96
|
-
|
|
113
|
+
u as B
|
|
97
114
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var o=Object.create;var l=Object.defineProperty;var f=Object.getOwnPropertyDescriptor;var d=Object.getOwnPropertyNames;var y=Object.getPrototypeOf,p=Object.prototype.hasOwnProperty;var g=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of d(e))!p.call(n,s)&&s!==t&&l(n,s,{get:()=>e[s],enumerable:!(r=f(e,s))||r.enumerable});return n};var m=(n,e,t)=>(t=n!=null?o(y(n)):{},g(e||!n||!n.__esModule?l(t,"default",{value:n,enumerable:!0}):t,n));const b=require("node:fs"),h=require("node:path"),F=require("node:url");function u(n){const e=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(n){for(const t in n)if(t!=="default"){const r=Object.getOwnPropertyDescriptor(n,t);Object.defineProperty(e,t,r.get?r:{enumerable:!0,get:()=>n[t]})}}return e.default=n,Object.freeze(e)}const i=u(b),a=u(h);class S{fileExists(e,t){return i.existsSync(a.join(e,t))}readFile(e,t){try{const r=a.join(e,t);if(i.existsSync(r))return i.readFileSync(r,"utf-8")}catch{}return null}readJSON(e,t){try{const r=this.readFile(e,t);if(r)return JSON.parse(r)}catch{}return null}getProjectName(e){return a.basename(e)}listFiles(e){try{return i.readdirSync(e)}catch{return[]}}getDirname(e){return a.dirname(F.fileURLToPath(e))}async loadTemplateFromFile(e){try{if(i.existsSync(e))return i.readFileSync(e,"utf-8")}catch{}return null}createTemplate(e,t,r,s={}){return{name:e,tags:t,content:r,...s}}parseDependencies(e,...t){const r={};for(const s of t){const c=e[s];c&&typeof c=="object"&&Object.assign(r,c)}return r}async findFiles(e,t){try{const{glob:r}=await import("glob");return await r(t,{cwd:e,absolute:!1})}catch{return[]}}async hasFiles(e,t){return(await this.findFiles(e,t)).length>0}}exports.BasePlugin=S;
|
package/dist/cpp/index.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
2
|
-
`);let o=!1,e=null;for(const
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const m=require("../BasePlugin-Be6rLq9o.cjs");var p=typeof document<"u"?document.currentScript:null;class d extends m.BasePlugin{constructor(){super(...arguments),this.name="cpp",this.displayName="C/C++",this.version="3.0.0",this.priority=85}async detect(t){const i=[],r={};let s,o=this.getProjectName(t);const e=await this.detectPlatformIO(t);if(e&&(i.push("platformio"),s="platformio.ini",e.board&&i.push(`board-${e.board}`),e.platform&&(i.push(`platform-${e.platform}`),e.platform.includes("espressif32")&&i.push("esp32"),e.platform.includes("espressif8266")&&i.push("esp8266"),e.platform.includes("atmelavr")&&i.push("arduino")),e.framework&&(i.push(`framework-${e.framework}`),e.framework==="arduino"&&i.push("arduino")),e.libs))for(const n of e.libs)r[n]="*";if(this.fileExists(t,"CMakeLists.txt")){i.push("cmake"),s||(s="CMakeLists.txt");const n=this.readFile(t,"CMakeLists.txt");if(n){const u=n.match(/project\s*\(\s*([^\s)]+)/i);u&&(o=u[1])}}return(this.fileExists(t,"Makefile")||this.fileExists(t,"makefile"))&&(i.push("makefile"),s||(s="Makefile")),await this.hasFiles(t,"*.ino")&&(i.push("arduino"),s||(s="*.ino")),await this.hasFiles(t,"**/*.{cpp,c,h,hpp}")&&(i.push("cpp"),s||(s="*.cpp")),i.length===0?null:{language:"C/C++",name:o,dependencies:r,manifestFile:s,projectPath:t,tags:i}}async detectPlatformIO(t){if(!this.fileExists(t,"platformio.ini"))return null;const i=this.readFile(t,"platformio.ini");if(!i)return null;const r={libs:[]},s=i.split(`
|
|
2
|
+
`);let o=!1,e=null;for(const f of s){const a=f.trim();if(a.startsWith("[env:")){o=!0,e=null;continue}if(a.startsWith("[")&&a.endsWith("]")){o=!1,e=null;continue}if(o||!r.board){const l=a.match(/^(\w+)\s*=\s*(.*)$/);if(l){const[,c,n]=l;switch(e=c,c){case"board":r.board=n.trim();break;case"platform":r.platform=n.trim();break;case"framework":r.framework=n.trim();break;case"lib_deps":n.trim()&&r.libs?.push(n.trim());break}}else e==="lib_deps"&&a&&!a.startsWith("[")&&r.libs?.push(a)}}return r.board||r.platform?r:null}getTemplates(){return this.getDirname(typeof document>"u"?require("url").pathToFileURL(__filename).href:p&&p.tagName.toUpperCase()==="SCRIPT"&&p.src||new URL("cpp/index.cjs",document.baseURI).href),[{name:"cpp-core",tags:["cpp"],content:this.getCppTemplate()},{name:"platformio-core",tags:["platformio"],content:this.getPlatformIOTemplate()},{name:"arduino-core",tags:["arduino"],content:this.getArduinoTemplate()}]}getDependencyTagMap(){return{boost:"boost",fmt:"fmt",spdlog:"spdlog",googletest:"gtest",catch2:"catch2","Adafruit GFX Library":"adafruit-gfx",WiFi:"wifi",ESP32:"esp32",FastLED:"fastled",ArduinoJson:"arduino-json"}}getConfigFileTagMap(){return{"platformio.ini":"platformio","CMakeLists.txt":"cmake",Makefile:"makefile",".clang-format":"clang-format",".clang-tidy":"clang-tidy"}}getCppTemplate(){return`# C/C++ Development Guidelines
|
|
3
3
|
|
|
4
4
|
This project uses C/C++.
|
|
5
5
|
|
|
@@ -76,4 +76,4 @@ This is an Arduino project.
|
|
|
76
76
|
- Check Serial Monitor baud rate
|
|
77
77
|
- Validate sensor readings
|
|
78
78
|
- Test incrementally
|
|
79
|
-
`}}exports.CppPlugin=
|
|
79
|
+
`}}exports.CppPlugin=d;
|
package/dist/cpp/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { B as u } from "../BasePlugin-
|
|
1
|
+
import { B as u } from "../BasePlugin-BXz3Zpy5.js";
|
|
2
2
|
class g extends u {
|
|
3
3
|
constructor() {
|
|
4
4
|
super(...arguments), this.name = "cpp", this.displayName = "C/C++", this.version = "3.0.0", this.priority = 85;
|
|
@@ -74,7 +74,7 @@ class g extends u {
|
|
|
74
74
|
return s.board || s.platform ? s : null;
|
|
75
75
|
}
|
|
76
76
|
getTemplates() {
|
|
77
|
-
return [
|
|
77
|
+
return this.getDirname(import.meta.url), [
|
|
78
78
|
{
|
|
79
79
|
name: "cpp-core",
|
|
80
80
|
tags: ["cpp"],
|
package/dist/csharp/index.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const p=require("node:path"),l=require("../BasePlugin-Be6rLq9o.cjs");var a=typeof document<"u"?document.currentScript:null;function u(n){const e=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(n){for(const t in n)if(t!=="default"){const r=Object.getOwnPropertyDescriptor(n,t);Object.defineProperty(e,t,r.get?r:{enumerable:!0,get:()=>n[t]})}}return e.default=n,Object.freeze(e)}const d=u(p);class g extends l.BasePlugin{constructor(){super(...arguments),this.name="csharp",this.displayName="C#",this.version="3.0.0",this.priority=60}async detect(e){const t=await this.findFiles(e,"*.csproj");if(t.length===0)return null;const r=t[0],s=this.readFile(e,r),i={};if(s){const o=s.matchAll(/<PackageReference\s+Include="([^"]+)"(?:\s+Version="([^"]+)")?/g);for(const c of o)i[c[1]]=c[2]||"*"}return{language:"C#",name:r.replace(".csproj",""),dependencies:i,manifestFile:r,projectPath:e}}getTemplates(){const e=this.getDirname(typeof document>"u"?require("url").pathToFileURL(__filename).href:a&&a.tagName.toUpperCase()==="SCRIPT"&&a.src||new URL("csharp/index.cjs",document.baseURI).href);return[{name:"csharp-core",tags:["csharp","dotnet"],content:()=>this.loadTemplateFromFile(d.join(e,"templates/lang-csharp.md")).then(t=>t||this.getCSharpFallbackTemplate())}]}getCSharpFallbackTemplate(){return`# C# / .NET Development Guidelines
|
|
2
2
|
|
|
3
3
|
This project uses C# and .NET.
|
|
4
4
|
|
package/dist/csharp/index.mjs
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import * as o from "node:path";
|
|
2
|
-
import { B as p } from "../BasePlugin-
|
|
3
|
-
class
|
|
2
|
+
import { B as p } from "../BasePlugin-BXz3Zpy5.js";
|
|
3
|
+
class m extends p {
|
|
4
4
|
constructor() {
|
|
5
5
|
super(...arguments), this.name = "csharp", this.displayName = "C#", this.version = "3.0.0", this.priority = 60;
|
|
6
6
|
}
|
|
7
7
|
async detect(e) {
|
|
8
|
-
const
|
|
9
|
-
if (
|
|
8
|
+
const t = await this.findFiles(e, "*.csproj");
|
|
9
|
+
if (t.length === 0)
|
|
10
10
|
return null;
|
|
11
|
-
const
|
|
11
|
+
const s = t[0], n = this.readFile(e, s), a = {};
|
|
12
12
|
if (n) {
|
|
13
13
|
const i = n.matchAll(
|
|
14
14
|
/<PackageReference\s+Include="([^"]+)"(?:\s+Version="([^"]+)")?/g
|
|
@@ -18,20 +18,21 @@ class g extends p {
|
|
|
18
18
|
}
|
|
19
19
|
return {
|
|
20
20
|
language: "C#",
|
|
21
|
-
name:
|
|
21
|
+
name: s.replace(".csproj", ""),
|
|
22
22
|
dependencies: a,
|
|
23
|
-
manifestFile:
|
|
23
|
+
manifestFile: s,
|
|
24
24
|
projectPath: e
|
|
25
25
|
};
|
|
26
26
|
}
|
|
27
27
|
getTemplates() {
|
|
28
|
+
const e = this.getDirname(import.meta.url);
|
|
28
29
|
return [
|
|
29
30
|
{
|
|
30
31
|
name: "csharp-core",
|
|
31
32
|
tags: ["csharp", "dotnet"],
|
|
32
33
|
content: () => this.loadTemplateFromFile(
|
|
33
|
-
o.join(
|
|
34
|
-
).then((
|
|
34
|
+
o.join(e, "templates/lang-csharp.md")
|
|
35
|
+
).then((t) => t || this.getCSharpFallbackTemplate())
|
|
35
36
|
}
|
|
36
37
|
];
|
|
37
38
|
}
|
|
@@ -64,5 +65,5 @@ This project uses C# and .NET.
|
|
|
64
65
|
}
|
|
65
66
|
}
|
|
66
67
|
export {
|
|
67
|
-
|
|
68
|
+
m as CSharpPlugin
|
|
68
69
|
};
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
# Flutter Development Guidelines
|
|
2
|
+
|
|
3
|
+
## Architecture & Structure
|
|
4
|
+
|
|
5
|
+
### Project Organization
|
|
6
|
+
```
|
|
7
|
+
lib/
|
|
8
|
+
├── main.dart
|
|
9
|
+
├── app.dart
|
|
10
|
+
├── core/
|
|
11
|
+
│ ├── constants/
|
|
12
|
+
│ ├── theme/
|
|
13
|
+
│ └── utils/
|
|
14
|
+
├── features/
|
|
15
|
+
│ ├── auth/
|
|
16
|
+
│ │ ├── data/
|
|
17
|
+
│ │ ├── domain/
|
|
18
|
+
│ │ └── presentation/
|
|
19
|
+
│ └── home/
|
|
20
|
+
└── shared/
|
|
21
|
+
├── widgets/
|
|
22
|
+
└── models/
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Widget Best Practices
|
|
26
|
+
|
|
27
|
+
### StatelessWidget
|
|
28
|
+
```dart
|
|
29
|
+
class UserCard extends StatelessWidget {
|
|
30
|
+
const UserCard({
|
|
31
|
+
super.key,
|
|
32
|
+
required this.user,
|
|
33
|
+
this.onTap,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
final User user;
|
|
37
|
+
final VoidCallback? onTap;
|
|
38
|
+
|
|
39
|
+
@override
|
|
40
|
+
Widget build(BuildContext context) {
|
|
41
|
+
return Card(
|
|
42
|
+
child: ListTile(
|
|
43
|
+
title: Text(user.name),
|
|
44
|
+
subtitle: Text(user.email),
|
|
45
|
+
onTap: onTap,
|
|
46
|
+
),
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### StatefulWidget
|
|
53
|
+
```dart
|
|
54
|
+
class Counter extends StatefulWidget {
|
|
55
|
+
const Counter({super.key});
|
|
56
|
+
|
|
57
|
+
@override
|
|
58
|
+
State<Counter> createState() => _CounterState();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
class _CounterState extends State<Counter> {
|
|
62
|
+
int _count = 0;
|
|
63
|
+
|
|
64
|
+
@override
|
|
65
|
+
void dispose() {
|
|
66
|
+
// Clean up resources
|
|
67
|
+
super.dispose();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@override
|
|
71
|
+
Widget build(BuildContext context) {
|
|
72
|
+
return Column(
|
|
73
|
+
children: [
|
|
74
|
+
Text('Count: $_count'),
|
|
75
|
+
ElevatedButton(
|
|
76
|
+
onPressed: () => setState(() => _count++),
|
|
77
|
+
child: const Text('Increment'),
|
|
78
|
+
),
|
|
79
|
+
],
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## State Management
|
|
86
|
+
|
|
87
|
+
### Riverpod (Recommended)
|
|
88
|
+
```dart
|
|
89
|
+
final userProvider = StateNotifierProvider<UserNotifier, AsyncValue<User>>((ref) {
|
|
90
|
+
return UserNotifier();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
class UserNotifier extends StateNotifier<AsyncValue<User>> {
|
|
94
|
+
UserNotifier() : super(const AsyncValue.loading());
|
|
95
|
+
|
|
96
|
+
Future<void> fetchUser(String id) async {
|
|
97
|
+
state = const AsyncValue.loading();
|
|
98
|
+
state = await AsyncValue.guard(() => api.getUser(id));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Usage in widget
|
|
103
|
+
class UserProfile extends ConsumerWidget {
|
|
104
|
+
@override
|
|
105
|
+
Widget build(BuildContext context, WidgetRef ref) {
|
|
106
|
+
final userState = ref.watch(userProvider);
|
|
107
|
+
|
|
108
|
+
return userState.when(
|
|
109
|
+
data: (user) => Text(user.name),
|
|
110
|
+
loading: () => CircularProgressIndicator(),
|
|
111
|
+
error: (err, stack) => Text('Error: $err'),
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Bloc Pattern
|
|
118
|
+
```dart
|
|
119
|
+
class UserBloc extends Bloc<UserEvent, UserState> {
|
|
120
|
+
UserBloc() : super(UserInitial()) {
|
|
121
|
+
on<LoadUser>(_onLoadUser);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
Future<void> _onLoadUser(LoadUser event, Emitter<UserState> emit) async {
|
|
125
|
+
emit(UserLoading());
|
|
126
|
+
try {
|
|
127
|
+
final user = await repository.getUser(event.id);
|
|
128
|
+
emit(UserLoaded(user));
|
|
129
|
+
} catch (e) {
|
|
130
|
+
emit(UserError(e.toString()));
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Navigation
|
|
137
|
+
|
|
138
|
+
### Named Routes
|
|
139
|
+
```dart
|
|
140
|
+
// Define routes
|
|
141
|
+
final routes = {
|
|
142
|
+
'/': (context) => HomeScreen(),
|
|
143
|
+
'/profile': (context) => ProfileScreen(),
|
|
144
|
+
'/settings': (context) => SettingsScreen(),
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
// Navigate
|
|
148
|
+
Navigator.pushNamed(context, '/profile');
|
|
149
|
+
|
|
150
|
+
// With arguments
|
|
151
|
+
Navigator.pushNamed(
|
|
152
|
+
context,
|
|
153
|
+
'/profile',
|
|
154
|
+
arguments: {'userId': '123'},
|
|
155
|
+
);
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Go Router (Modern)
|
|
159
|
+
```dart
|
|
160
|
+
final router = GoRouter(
|
|
161
|
+
routes: [
|
|
162
|
+
GoRoute(
|
|
163
|
+
path: '/',
|
|
164
|
+
builder: (context, state) => HomeScreen(),
|
|
165
|
+
),
|
|
166
|
+
GoRoute(
|
|
167
|
+
path: '/profile/:id',
|
|
168
|
+
builder: (context, state) {
|
|
169
|
+
final id = state.pathParameters['id']!;
|
|
170
|
+
return ProfileScreen(userId: id);
|
|
171
|
+
},
|
|
172
|
+
),
|
|
173
|
+
],
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
// Navigate
|
|
177
|
+
context.go('/profile/123');
|
|
178
|
+
context.push('/settings');
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Performance Optimization
|
|
182
|
+
|
|
183
|
+
### const Constructors
|
|
184
|
+
```dart
|
|
185
|
+
// Use const wherever possible
|
|
186
|
+
const Text('Hello');
|
|
187
|
+
const Padding(padding: EdgeInsets.all(8.0));
|
|
188
|
+
const SizedBox(height: 16);
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### ListView.builder
|
|
192
|
+
```dart
|
|
193
|
+
// For large lists, use builder
|
|
194
|
+
ListView.builder(
|
|
195
|
+
itemCount: items.length,
|
|
196
|
+
itemBuilder: (context, index) {
|
|
197
|
+
return ListTile(title: Text(items[index]));
|
|
198
|
+
},
|
|
199
|
+
);
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Keys
|
|
203
|
+
```dart
|
|
204
|
+
// Use keys for widgets that need to preserve state
|
|
205
|
+
ListView(
|
|
206
|
+
children: items.map((item) =>
|
|
207
|
+
ListTile(
|
|
208
|
+
key: ValueKey(item.id),
|
|
209
|
+
title: Text(item.name),
|
|
210
|
+
)
|
|
211
|
+
).toList(),
|
|
212
|
+
);
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## Responsive Design
|
|
216
|
+
|
|
217
|
+
### MediaQuery
|
|
218
|
+
```dart
|
|
219
|
+
final size = MediaQuery.of(context).size;
|
|
220
|
+
final isPortrait = size.height > size.width;
|
|
221
|
+
|
|
222
|
+
if (size.width > 600) {
|
|
223
|
+
// Tablet layout
|
|
224
|
+
} else {
|
|
225
|
+
// Mobile layout
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### LayoutBuilder
|
|
230
|
+
```dart
|
|
231
|
+
LayoutBuilder(
|
|
232
|
+
builder: (context, constraints) {
|
|
233
|
+
if (constraints.maxWidth > 600) {
|
|
234
|
+
return WideLayout();
|
|
235
|
+
}
|
|
236
|
+
return NarrowLayout();
|
|
237
|
+
},
|
|
238
|
+
);
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## Testing
|
|
242
|
+
|
|
243
|
+
### Widget Tests
|
|
244
|
+
```dart
|
|
245
|
+
testWidgets('Counter increments', (tester) async {
|
|
246
|
+
await tester.pumpWidget(
|
|
247
|
+
MaterialApp(home: Counter()),
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
expect(find.text('0'), findsOneWidget);
|
|
251
|
+
|
|
252
|
+
await tester.tap(find.byType(ElevatedButton));
|
|
253
|
+
await tester.pump();
|
|
254
|
+
|
|
255
|
+
expect(find.text('1'), findsOneWidget);
|
|
256
|
+
});
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Integration Tests
|
|
260
|
+
```dart
|
|
261
|
+
void main() {
|
|
262
|
+
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
|
263
|
+
|
|
264
|
+
testWidgets('Complete user flow', (tester) async {
|
|
265
|
+
await tester.pumpWidget(MyApp());
|
|
266
|
+
|
|
267
|
+
// Login
|
|
268
|
+
await tester.enterText(find.byKey(Key('email')), 'test@test.com');
|
|
269
|
+
await tester.tap(find.byKey(Key('login')));
|
|
270
|
+
await tester.pumpAndSettle();
|
|
271
|
+
|
|
272
|
+
// Verify home screen
|
|
273
|
+
expect(find.text('Welcome'), findsOneWidget);
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
## Best Practices
|
|
279
|
+
|
|
280
|
+
### Widget Composition
|
|
281
|
+
- Break large widgets into smaller, reusable components
|
|
282
|
+
- Extract complex logic into separate methods or classes
|
|
283
|
+
- Use const constructors to improve performance
|
|
284
|
+
|
|
285
|
+
### Error Handling
|
|
286
|
+
- Use ErrorWidget.builder for custom error widgets
|
|
287
|
+
- Wrap risky operations in try-catch
|
|
288
|
+
- Provide meaningful error messages to users
|
|
289
|
+
|
|
290
|
+
### Asset Management
|
|
291
|
+
```yaml
|
|
292
|
+
# pubspec.yaml
|
|
293
|
+
flutter:
|
|
294
|
+
assets:
|
|
295
|
+
- assets/images/
|
|
296
|
+
- assets/icons/
|
|
297
|
+
fonts:
|
|
298
|
+
- family: Roboto
|
|
299
|
+
fonts:
|
|
300
|
+
- asset: fonts/Roboto-Regular.ttf
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### Platform-Specific Code
|
|
304
|
+
```dart
|
|
305
|
+
import 'dart:io' show Platform;
|
|
306
|
+
|
|
307
|
+
if (Platform.isAndroid) {
|
|
308
|
+
// Android-specific
|
|
309
|
+
} else if (Platform.isIOS) {
|
|
310
|
+
// iOS-specific
|
|
311
|
+
}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
## Common Patterns
|
|
315
|
+
|
|
316
|
+
### Dependency Injection
|
|
317
|
+
```dart
|
|
318
|
+
class MyApp extends StatelessWidget {
|
|
319
|
+
final ApiService apiService;
|
|
320
|
+
final AuthService authService;
|
|
321
|
+
|
|
322
|
+
const MyApp({
|
|
323
|
+
required this.apiService,
|
|
324
|
+
required this.authService,
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### Service Locator
|
|
330
|
+
```dart
|
|
331
|
+
final getIt = GetIt.instance;
|
|
332
|
+
|
|
333
|
+
void setupLocator() {
|
|
334
|
+
getIt.registerSingleton<ApiService>(ApiService());
|
|
335
|
+
getIt.registerLazySingleton<AuthService>(() => AuthService());
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Usage
|
|
339
|
+
final api = getIt<ApiService>();
|
|
340
|
+
```
|