diff --git a/tdesign-component/coverage/lcov.info b/tdesign-component/coverage/lcov.info index e24c20947..7c52f5e7e 100644 --- a/tdesign-component/coverage/lcov.info +++ b/tdesign-component/coverage/lcov.info @@ -1,36 +1,62 @@ -SF:lib/src/components/picker/t_picker_normalize.dart -DA:9,0 -DA:12,1 -DA:14,1 -DA:17,1 -DA:18,1 -DA:19,1 -DA:20,3 -DA:22,1 -DA:27,1 -DA:29,1 -DA:32,1 -DA:35,1 -DA:36,1 -DA:39,1 -DA:40,3 -DA:41,2 -DA:42,2 -DA:43,1 -DA:44,1 -DA:46,2 -DA:49,1 -DA:51,1 -DA:52,2 -DA:53,1 -DA:54,2 -DA:59,1 -DA:60,1 -DA:61,1 -DA:63,1 -DA:64,3 -LF:30 -LH:29 +SF:lib/src/components/popup/t_popup_config.dart +DA:7,4 +DA:47,4 +DA:85,4 +DA:86,4 +DA:89,4 +DA:175,4 +DA:176,8 +DA:177,8 +DA:180,4 +DA:181,8 +DA:182,8 +DA:185,4 +DA:186,12 +DA:189,4 +DA:190,8 +DA:191,8 +DA:192,8 +DA:195,4 +DA:196,8 +DA:197,4 +DA:198,8 +DA:200,6 +DA:203,2 +DA:204,4 +DA:205,4 +DA:206,2 +DA:207,2 +DA:208,7 +DA:210,2 +DA:211,4 +DA:212,2 +DA:213,6 +DA:215,4 +DA:216,8 +DA:217,4 +DA:218,4 +DA:219,4 +DA:220,2 +DA:221,2 +DA:222,2 +DA:226,4 +DA:227,5 +DA:228,4 +DA:229,4 +DA:233,3 +DA:234,3 +DA:235,3 +DA:236,2 +DA:237,2 +DA:242,8 +DA:243,3 +DA:244,3 +DA:245,3 +DA:246,3 +DA:247,2 +DA:252,4 +LF:56 +LH:56 end_of_record SF:lib/src/components/action_sheet/t_action_sheet.dart DA:20,0 @@ -66,140 +92,134 @@ DA:246,0 DA:250,0 DA:251,0 DA:254,0 -DA:257,0 -DA:262,0 -DA:287,0 +DA:256,0 +DA:260,0 +DA:281,0 +DA:285,0 DA:289,0 -DA:293,0 -DA:295,0 -DA:296,0 -DA:306,0 -DA:307,0 -DA:323,0 -DA:324,0 -DA:340,0 -DA:342,0 -LF:46 +DA:290,0 +DA:301,0 +DA:302,0 +DA:319,0 +DA:320,0 +DA:334,0 +LF:44 LH:0 end_of_record SF:lib/src/util/context_extension.dart -DA:7,0 +DA:7,9 LF:1 -LH:0 +LH:1 end_of_record -SF:lib/src/components/popup/t_popup_route.dart -DA:14,0 -DA:74,0 -DA:76,0 -DA:79,0 -DA:82,0 -DA:83,0 -DA:88,0 -DA:89,0 -DA:101,0 -DA:108,0 -DA:109,0 -DA:110,0 -DA:111,0 -DA:112,0 -DA:114,0 -DA:116,0 -DA:117,0 -DA:118,0 -DA:119,0 -DA:120,0 -DA:121,0 -DA:122,0 -DA:125,0 -DA:126,0 -DA:127,0 -DA:128,0 -DA:135,0 -DA:137,0 -DA:138,0 -DA:139,0 -DA:140,0 -DA:141,0 -DA:142,0 -DA:143,0 -DA:147,0 -DA:148,0 -DA:158,0 -DA:160,0 -DA:161,0 -DA:166,0 -DA:168,0 -DA:169,0 -DA:170,0 -DA:171,0 -DA:172,0 -DA:175,0 -DA:178,0 -DA:180,0 -DA:181,0 -DA:185,0 -DA:186,0 -DA:190,0 -DA:191,0 -DA:194,0 -DA:196,0 -DA:197,0 -DA:198,0 -DA:199,0 -DA:200,0 -DA:201,0 -DA:204,0 -DA:207,0 -DA:209,0 -DA:210,0 -DA:213,0 -DA:215,0 -DA:216,0 -DA:217,0 -DA:219,0 -DA:220,0 -DA:221,0 -DA:223,0 -DA:224,0 -DA:229,0 -DA:230,0 -DA:231,0 -DA:232,0 -DA:233,0 -DA:234,0 -DA:236,0 -DA:238,0 -DA:243,0 -DA:245,0 -DA:246,0 -DA:247,0 -DA:248,0 -DA:249,0 -DA:250,0 -DA:251,0 -DA:252,0 -DA:259,0 -DA:261,0 -DA:263,0 -DA:265,0 -DA:267,0 -DA:269,0 -DA:278,0 -DA:280,0 -DA:282,0 -DA:283,0 -DA:284,0 -DA:285,0 -DA:286,0 -DA:287,0 -DA:288,0 -DA:289,0 -DA:290,0 -DA:292,0 -DA:296,0 -DA:298,0 -LF:110 -LH:0 +SF:lib/src/components/popup/t_popup.dart +DA:19,1 +DA:187,3 +DA:228,3 +DA:266,3 +DA:269,3 +DA:274,3 +DA:276,2 +DA:277,4 +DA:284,3 +DA:285,3 +DA:288,3 +DA:289,3 +DA:290,3 +DA:293,3 +DA:298,3 +DA:303,3 +DA:305,9 +DA:306,3 +DA:307,3 +DA:316,3 +DA:317,3 +DA:318,3 +DA:319,6 +DA:320,3 +DA:324,1 +DA:325,1 +DA:331,1 +DA:333,1 +DA:334,2 +DA:335,4 +DA:339,1 +DA:341,2 +DA:342,1 +DA:345,1 +DA:346,2 +DA:349,2 +DA:350,1 +DA:351,3 +DA:352,2 +DA:353,2 +DA:354,2 +DA:355,2 +DA:356,2 +DA:357,2 +DA:358,2 +DA:359,2 +DA:360,2 +DA:361,2 +DA:362,2 +DA:363,2 +DA:364,2 +DA:365,2 +DA:366,2 +DA:367,2 +DA:368,2 +DA:369,2 +DA:370,2 +DA:371,2 +DA:372,2 +DA:373,2 +DA:374,2 +DA:375,2 +DA:376,2 +DA:377,2 +DA:378,2 +DA:379,2 +DA:380,2 +DA:381,2 +DA:382,2 +DA:383,2 +DA:384,2 +DA:385,2 +DA:386,2 +DA:387,2 +DA:388,2 +DA:392,1 +DA:394,2 +LF:77 +LH:77 +end_of_record +SF:lib/src/components/popup/t_popup_tracker.dart +DA:5,9 +DA:7,3 +DA:8,15 +DA:11,3 +DA:12,9 +DA:13,9 +DA:14,6 +DA:18,3 +DA:19,6 +DA:20,3 +DA:23,3 +LF:11 +LH:11 +end_of_record +SF:lib/src/components/popup/t_popup_handle.dart +DA:5,3 +DA:18,9 +DA:23,3 +DA:24,3 +DA:27,6 +DA:30,3 +DA:31,3 +DA:34,3 +DA:35,3 +DA:36,3 +LF:10 +LH:10 end_of_record SF:lib/src/components/action_sheet/t_action_sheet_grid.dart DA:30,0 @@ -483,7 +503,7 @@ DA:48,0 DA:51,0 DA:54,0 DA:57,0 -DA:60,3 +DA:60,9 DA:63,0 DA:68,0 DA:71,0 @@ -533,11 +553,11 @@ DA:204,0 DA:207,0 DA:210,0 DA:213,0 -DA:218,3 -DA:221,3 +DA:218,0 +DA:221,0 DA:224,0 DA:227,0 -DA:230,0 +DA:230,6 DA:233,0 DA:236,0 DA:239,0 @@ -557,11 +577,11 @@ DA:280,0 DA:283,0 DA:286,0 DA:290,0 -DA:292,0 +DA:292,9 DA:294,0 DA:296,0 DA:298,0 -DA:300,3 +DA:300,0 DA:302,0 DA:303,0 DA:305,0 @@ -572,15 +592,15 @@ DA:312,0 DA:314,0 DA:316,0 DA:318,0 -DA:322,3 -DA:324,0 -DA:326,3 -DA:328,3 +DA:322,9 +DA:324,9 +DA:326,0 +DA:328,0 DA:330,0 DA:332,0 DA:334,0 LF:110 -LH:7 +LH:5 end_of_record SF:lib/src/theme/t_fonts.dart DA:8,0 @@ -589,11 +609,11 @@ DA:14,0 DA:17,0 DA:20,0 DA:23,0 -DA:26,0 -DA:29,3 +DA:26,9 +DA:29,9 DA:32,0 DA:35,0 -DA:38,3 +DA:38,9 DA:41,0 DA:44,0 DA:47,0 @@ -605,13 +625,13 @@ DA:62,0 DA:65,0 DA:68,0 LF:21 -LH:2 +LH:3 end_of_record SF:lib/src/theme/t_radius.dart DA:8,0 -DA:9,3 +DA:9,0 DA:10,0 -DA:11,0 +DA:11,9 DA:14,0 DA:17,0 LF:6 @@ -619,45 +639,45 @@ LH:1 end_of_record SF:lib/src/theme/t_spacers.dart DA:5,0 -DA:7,3 -DA:9,3 +DA:7,9 +DA:9,9 DA:11,3 DA:13,0 -DA:15,3 +DA:15,0 DA:17,0 DA:19,0 DA:21,0 DA:23,0 DA:25,0 LF:11 -LH:4 +LH:3 end_of_record SF:lib/src/theme/t_theme.dart -DA:13,0 -DA:18,0 -DA:35,0 -DA:38,0 -DA:40,0 -DA:41,0 -DA:42,0 -DA:43,0 -DA:44,0 +DA:13,3 +DA:18,3 +DA:35,3 +DA:38,3 +DA:40,6 +DA:41,3 +DA:42,3 +DA:43,3 +DA:44,3 DA:48,0 -DA:55,0 -DA:59,0 +DA:55,3 +DA:59,6 DA:63,0 DA:64,0 -DA:69,1 -DA:72,1 +DA:69,3 +DA:72,0 DA:76,0 DA:77,0 DA:79,0 DA:80,0 DA:86,0 DA:89,0 -DA:136,1 -DA:149,1 -DA:152,1 +DA:136,3 +DA:149,3 +DA:152,3 DA:158,0 DA:159,0 DA:167,0 @@ -695,64 +715,64 @@ DA:245,0 DA:247,0 DA:248,0 DA:251,0 -DA:257,1 -DA:261,1 -DA:262,1 -DA:264,1 -DA:265,1 -DA:266,1 -DA:268,1 -DA:269,1 -DA:270,4 -DA:284,1 -DA:291,1 +DA:257,3 +DA:261,3 +DA:262,3 +DA:264,3 +DA:265,3 +DA:266,3 +DA:268,3 +DA:269,3 +DA:270,12 +DA:284,3 +DA:291,3 DA:292,0 -DA:297,1 -DA:298,1 -DA:299,1 -DA:300,1 +DA:297,3 +DA:298,3 +DA:299,3 +DA:300,3 DA:301,0 -DA:302,1 -DA:304,1 -DA:305,1 -DA:306,1 -DA:308,3 -DA:309,2 +DA:302,3 +DA:304,3 +DA:305,3 +DA:306,3 +DA:308,9 +DA:309,6 DA:332,0 DA:333,0 DA:337,0 -DA:342,1 -DA:343,1 -DA:344,2 -DA:345,1 -DA:350,1 -DA:351,2 -DA:352,1 -DA:354,2 -DA:359,1 -DA:360,2 -DA:361,2 -DA:365,1 -DA:366,2 -DA:367,3 -DA:371,1 -DA:372,2 -DA:373,3 -DA:377,1 -DA:378,2 -DA:379,3 -DA:383,1 -DA:384,2 -DA:385,1 -DA:386,2 -DA:387,2 -DA:388,2 -DA:389,2 -DA:390,2 -DA:391,4 -DA:392,3 -DA:396,2 -DA:400,1 +DA:342,3 +DA:343,3 +DA:344,6 +DA:345,3 +DA:350,3 +DA:351,6 +DA:352,3 +DA:354,6 +DA:359,3 +DA:360,6 +DA:361,6 +DA:365,3 +DA:366,6 +DA:367,9 +DA:371,3 +DA:372,6 +DA:373,9 +DA:377,3 +DA:378,6 +DA:379,9 +DA:383,3 +DA:384,6 +DA:385,3 +DA:386,6 +DA:387,6 +DA:388,6 +DA:389,6 +DA:390,6 +DA:391,12 +DA:392,9 +DA:396,6 +DA:400,3 DA:401,0 DA:402,0 DA:406,0 @@ -781,17 +801,17 @@ DA:456,0 DA:457,0 DA:458,0 DA:459,0 -DA:474,1 -DA:477,2 -DA:481,1 -DA:484,2 -DA:485,1 -DA:489,3 -DA:490,1 -DA:496,1 -DA:497,1 +DA:474,3 +DA:477,6 +DA:481,3 +DA:484,6 +DA:485,3 +DA:489,9 +DA:490,3 +DA:496,3 +DA:497,3 LF:157 -LH:67 +LH:77 end_of_record SF:lib/src/util/iterable_ext.dart DA:8,0 @@ -842,7 +862,7 @@ LF:14 LH:0 end_of_record SF:lib/src/components/badge/t_badge.dart -DA:43,1 +DA:43,5 DA:57,0 DA:95,0 DA:96,0 @@ -966,15 +986,15 @@ LF:120 LH:1 end_of_record SF:lib/src/components/text/t_text.dart -DA:41,1 -DA:68,1 +DA:41,3 +DA:68,3 DA:71,0 DA:98,0 -DA:163,1 -DA:165,1 +DA:163,3 +DA:165,3 DA:167,0 DA:169,0 -DA:172,1 +DA:172,3 DA:173,0 DA:174,0 DA:176,0 @@ -992,68 +1012,68 @@ DA:192,0 DA:193,0 DA:194,0 DA:196,0 -DA:199,3 -DA:201,1 +DA:199,6 +DA:201,3 DA:203,0 DA:205,0 -DA:210,1 -DA:211,1 -DA:214,1 -DA:216,1 -DA:217,2 +DA:210,3 +DA:211,3 +DA:214,3 +DA:216,3 +DA:217,0 DA:218,0 -DA:220,2 -DA:221,3 -DA:223,1 +DA:220,6 +DA:221,6 +DA:223,3 DA:224,0 DA:225,0 -DA:227,2 -DA:229,1 +DA:227,6 +DA:229,3 DA:230,0 DA:232,0 -DA:237,2 -DA:238,1 -DA:239,2 -DA:244,2 -DA:245,2 -DA:246,2 -DA:247,2 -DA:248,2 -DA:249,2 -DA:250,3 -DA:251,2 -DA:252,2 -DA:253,2 -DA:254,2 -DA:255,2 -DA:256,2 -DA:257,2 -DA:258,1 -DA:259,3 -DA:260,2 -DA:261,2 -DA:262,2 -DA:265,2 -DA:266,1 +DA:237,6 +DA:238,3 +DA:239,3 +DA:244,6 +DA:245,9 +DA:246,3 +DA:247,3 +DA:248,3 +DA:249,3 +DA:250,6 +DA:251,3 +DA:252,3 +DA:253,3 +DA:254,3 +DA:255,3 +DA:256,3 +DA:257,3 +DA:258,3 +DA:259,6 +DA:260,3 +DA:261,3 +DA:262,3 +DA:265,3 +DA:266,3 DA:272,0 DA:273,0 -DA:276,1 -DA:280,1 -DA:281,1 -DA:282,1 -DA:283,1 -DA:285,1 -DA:286,1 -DA:287,1 -DA:288,1 -DA:289,1 -DA:290,1 -DA:291,1 -DA:292,1 -DA:293,1 -DA:294,1 -DA:295,1 -DA:296,1 +DA:276,3 +DA:280,3 +DA:281,3 +DA:282,3 +DA:283,3 +DA:285,3 +DA:286,3 +DA:287,3 +DA:288,3 +DA:289,3 +DA:290,3 +DA:291,3 +DA:292,3 +DA:293,3 +DA:294,3 +DA:295,3 +DA:296,3 DA:298,0 DA:299,0 DA:301,0 @@ -1141,7 +1161,7 @@ DA:497,0 DA:502,0 DA:505,0 LF:174 -LH:58 +LH:57 end_of_record SF:lib/src/components/action_sheet/t_action_sheet_item_widget.dart DA:12,0 @@ -1944,96 +1964,92 @@ LF:90 LH:0 end_of_record SF:lib/src/components/picker/no_wave_behavior.dart -DA:11,1 -DA:14,2 -DA:17,1 -DA:21,1 -DA:23,1 -DA:24,1 -DA:25,1 -DA:26,1 -DA:27,1 -DA:28,1 +DA:11,0 +DA:14,0 +DA:17,0 +DA:21,0 +DA:23,0 +DA:24,0 +DA:25,0 +DA:26,0 +DA:27,0 +DA:28,0 LF:10 -LH:10 +LH:0 end_of_record SF:lib/src/components/picker/t_item_widget.dart -DA:31,1 -DA:67,1 -DA:68,1 -DA:70,1 -DA:72,1 -DA:74,1 -DA:75,1 -DA:76,1 -DA:78,1 -DA:79,1 -DA:82,1 -DA:84,1 -DA:85,2 -DA:92,1 -DA:94,1 -DA:95,1 -DA:96,1 -DA:98,7 -DA:99,1 -DA:100,1 -DA:101,1 -DA:102,2 -DA:104,1 -DA:105,1 -DA:106,1 -DA:110,1 -DA:111,1 -DA:114,1 -DA:115,1 -DA:116,1 -DA:117,1 -DA:133,1 -DA:150,3 -DA:153,1 -DA:154,1 -DA:155,1 -DA:156,1 -DA:157,2 -DA:158,1 -DA:161,1 -DA:164,1 -DA:168,1 -DA:169,2 -DA:172,1 -DA:173,3 -DA:174,3 -DA:178,1 -DA:179,2 +DA:31,0 +DA:67,0 +DA:68,0 +DA:70,0 +DA:72,0 +DA:74,0 +DA:75,0 +DA:76,0 +DA:78,0 +DA:79,0 +DA:82,0 +DA:84,0 +DA:85,0 +DA:92,0 +DA:94,0 +DA:95,0 +DA:96,0 +DA:98,0 +DA:99,0 +DA:100,0 +DA:101,0 +DA:102,0 +DA:104,0 +DA:105,0 +DA:106,0 +DA:110,0 +DA:111,0 +DA:114,0 +DA:115,0 +DA:116,0 +DA:117,0 +DA:133,5 +DA:150,0 +DA:153,0 +DA:154,0 +DA:155,0 +DA:156,0 +DA:157,0 +DA:158,0 +DA:161,0 +DA:164,0 +DA:168,0 +DA:169,0 +DA:172,0 +DA:173,0 +DA:174,0 +DA:178,0 +DA:179,0 LF:48 -LH:48 +LH:1 end_of_record SF:lib/src/components/picker/t_picker_option.dart -DA:14,2 -DA:29,1 -DA:32,1 -DA:33,3 -DA:34,3 -DA:35,3 -DA:36,3 -DA:38,1 -DA:39,4 -DA:41,1 -DA:42,3 +DA:14,0 +DA:29,0 +DA:32,0 +DA:33,0 +DA:34,0 +DA:35,0 +DA:36,0 +DA:38,0 +DA:39,0 +DA:41,0 +DA:42,0 LF:11 -LH:11 +LH:0 end_of_record SF:lib/src/components/picker/t_picker_value.dart -DA:13,1 -DA:28,1 -DA:29,5 -DA:35,1 -DA:36,5 -DA:38,1 -DA:40,4 -LF:7 -LH:7 +DA:13,0 +DA:38,0 +DA:40,0 +LF:3 +LH:0 end_of_record SF:lib/src/components/calendar/t_calendar.dart DA:26,0 @@ -2211,68 +2227,68 @@ LF:171 LH:0 end_of_record SF:lib/src/components/calendar/t_date_picker.dart -DA:21,0 -DA:34,0 +DA:22,0 DA:35,0 -DA:41,0 -DA:43,0 +DA:36,0 +DA:42,0 DA:44,0 DA:45,0 -DA:48,0 -DA:50,0 -DA:52,0 +DA:46,0 +DA:49,0 +DA:51,0 DA:53,0 DA:54,0 DA:55,0 DA:56,0 -DA:58,0 +DA:57,0 DA:59,0 DA:60,0 DA:61,0 -DA:63,0 +DA:62,0 DA:64,0 DA:65,0 DA:66,0 DA:67,0 -DA:69,0 -DA:74,0 +DA:68,0 +DA:70,0 DA:75,0 -DA:77,0 -DA:79,0 +DA:76,0 +DA:78,0 DA:80,0 DA:81,0 -DA:84,0 -DA:86,0 +DA:82,0 +DA:85,0 DA:87,0 DA:88,0 -DA:92,0 -DA:94,0 +DA:89,0 +DA:93,0 DA:95,0 DA:96,0 DA:97,0 -DA:108,0 +DA:98,0 DA:109,0 DA:110,0 -DA:112,0 +DA:111,0 DA:113,0 -DA:115,0 +DA:114,0 DA:116,0 DA:117,0 DA:118,0 -DA:120,0 -DA:122,0 -DA:124,0 +DA:119,0 +DA:121,0 +DA:123,0 DA:125,0 -DA:127,0 -DA:129,0 +DA:126,0 +DA:128,0 DA:130,0 DA:131,0 DA:132,0 DA:133,0 -DA:135,0 -DA:137,0 -DA:139,0 -DA:142,0 +DA:134,0 +DA:136,0 +DA:138,0 +DA:140,0 +DA:143,0 LF:62 LH:0 end_of_record @@ -2637,36 +2653,36 @@ DA:25,0 DA:61,0 DA:64,0 DA:67,0 +DA:68,0 DA:71,0 -DA:74,0 -DA:75,0 -DA:76,0 +DA:72,0 +DA:73,0 DA:77,0 +DA:79,0 DA:80,0 DA:81,0 -DA:82,0 -DA:83,0 +DA:84,0 DA:85,0 DA:86,0 -DA:87,0 -DA:92,0 -DA:93,0 +DA:88,0 +DA:89,0 +DA:90,0 +DA:96,0 DA:97,0 DA:98,0 -DA:99,0 +DA:102,0 DA:103,0 DA:104,0 DA:105,0 -DA:106,0 +DA:110,0 DA:111,0 -DA:113,0 -DA:118,0 -DA:120,0 -DA:125,0 -DA:133,0 -DA:141,0 -DA:146,0 -DA:147,0 +DA:114,0 +DA:116,0 +DA:121,0 +DA:129,0 +DA:137,0 +DA:142,0 +DA:143,0 LF:38 LH:0 end_of_record @@ -3972,7 +3988,7 @@ LF:10 LH:0 end_of_record SF:lib/src/components/collapse/t_inset_divider.dart -DA:9,1 +DA:9,5 DA:11,0 DA:13,0 DA:15,0 @@ -4441,7 +4457,7 @@ LF:47 LH:0 end_of_record SF:lib/src/components/divider/t_divider.dart -DA:12,6 +DA:12,30 DA:26,0 DA:64,0 DA:67,0 @@ -4498,10 +4514,9 @@ DA:108,0 DA:109,0 DA:111,0 DA:112,0 +DA:113,0 +DA:116,0 DA:117,0 -DA:118,0 -DA:119,0 -DA:120,0 DA:121,0 DA:122,0 DA:123,0 @@ -4513,22 +4528,21 @@ DA:128,0 DA:129,0 DA:130,0 DA:131,0 -DA:136,0 -DA:138,0 -DA:142,0 +DA:132,0 +DA:133,0 +DA:134,0 +DA:139,0 +DA:140,0 DA:143,0 -DA:146,0 +DA:145,0 DA:148,0 DA:149,0 DA:150,0 -DA:154,0 -DA:155,0 -DA:156,0 -LF:35 +LF:33 LH:0 end_of_record SF:lib/src/components/icon/t_icons.dart -DA:8,1 +DA:8,5 DA:9,0 DA:22,0 LF:3 @@ -6446,11 +6460,11 @@ LF:518 LH:0 end_of_record SF:lib/src/components/image_viewer/t_image_viewer.dart -DA:12,0 +DA:11,0 +DA:40,0 DA:41,0 -DA:42,0 +DA:46,0 DA:47,0 -DA:48,0 LF:5 LH:0 end_of_record @@ -6916,7 +6930,7 @@ DA:94,0 DA:98,0 DA:102,0 DA:104,0 -DA:111,1 +DA:111,5 DA:120,0 DA:125,0 DA:129,0 @@ -7977,7 +7991,7 @@ LF:47 LH:0 end_of_record SF:lib/src/components/loading/t_loading.dart -DA:38,3 +DA:38,15 DA:49,0 DA:78,0 DA:80,0 @@ -8481,12 +8495,12 @@ LF:21 LH:0 end_of_record SF:lib/src/util/platform_util.dart -DA:6,1 -DA:7,1 -DA:10,1 -DA:11,1 -DA:14,1 -DA:15,1 +DA:6,0 +DA:7,0 +DA:10,3 +DA:11,3 +DA:14,0 +DA:15,0 DA:18,0 DA:19,0 DA:22,0 @@ -8497,409 +8511,23 @@ DA:30,0 DA:31,0 DA:34,0 LF:15 -LH:6 +LH:2 end_of_record SF:lib/src/components/picker/t_picker.dart -DA:57,2 -DA:229,1 -DA:230,1 -DA:250,6 -DA:252,1 -DA:254,1 -DA:255,1 -DA:258,1 -DA:260,1 -DA:261,4 -DA:263,4 -DA:265,1 -DA:266,1 -DA:270,1 -DA:272,1 -DA:273,1 -DA:277,1 -DA:278,2 -DA:279,1 -DA:283,1 -DA:284,2 -DA:285,2 -DA:286,2 -DA:287,2 -DA:288,2 -DA:290,2 -DA:291,2 -DA:292,1 -DA:293,1 -DA:294,2 -DA:295,1 -DA:296,1 -DA:300,1 -DA:301,1 -DA:303,2 -DA:304,5 -DA:306,2 -DA:308,7 -DA:309,1 -DA:313,1 -DA:317,1 -DA:318,2 -DA:319,1 -DA:323,2 -DA:324,1 -DA:327,1 -DA:328,2 -DA:331,5 -DA:332,1 -DA:337,1 -DA:339,1 -DA:340,1 -DA:342,1 -DA:343,1 -DA:344,1 -DA:350,1 -DA:351,1 -DA:352,2 -DA:353,1 -DA:354,2 -DA:355,1 -DA:356,2 -DA:358,1 -DA:360,1 -DA:362,1 -DA:363,5 -DA:364,1 -DA:365,1 -DA:366,1 -DA:367,1 -DA:368,1 -DA:369,1 -DA:370,2 -DA:375,1 -DA:376,2 -DA:377,1 -DA:378,1 -DA:379,4 -DA:380,2 -DA:392,1 -DA:393,1 -DA:395,1 -DA:396,2 -DA:397,1 -DA:398,1 -DA:399,1 -DA:401,1 -DA:402,4 -DA:403,4 -DA:404,1 -DA:405,2 -DA:407,1 -DA:408,2 -DA:410,1 -DA:412,1 -DA:413,4 -DA:414,5 -DA:415,1 -DA:416,2 -DA:425,1 -DA:426,2 -DA:427,2 -DA:429,1 -DA:430,2 -DA:431,1 -DA:432,2 -DA:434,1 -DA:446,1 -DA:454,1 -DA:455,1 -DA:456,2 -DA:459,1 -DA:460,1 -DA:465,1 -DA:467,2 -DA:468,2 -DA:469,0 -DA:471,1 -DA:474,1 -DA:475,1 -DA:476,2 -DA:483,1 -DA:484,2 -DA:485,1 -DA:489,1 -DA:490,1 -DA:492,1 -DA:493,1 -DA:494,1 -DA:495,1 -DA:496,1 -DA:497,1 -DA:498,1 -DA:500,2 -DA:501,2 -DA:504,2 -DA:506,2 -DA:507,1 -DA:508,1 -DA:509,2 -DA:510,2 -DA:511,2 -DA:514,1 -DA:515,2 -DA:516,2 -DA:517,2 -DA:526,1 -DA:529,1 -DA:533,2 -DA:534,1 -DA:537,1 -DA:538,2 -DA:539,2 -DA:543,1 -DA:545,2 -DA:550,2 -DA:554,1 -DA:559,1 -DA:561,3 -DA:562,1 -DA:565,2 -DA:568,1 -DA:569,5 -DA:570,0 -DA:574,2 -DA:575,1 -DA:579,1 -DA:581,1 -DA:584,2 -DA:585,1 -DA:586,2 -DA:587,1 -DA:589,1 -DA:590,0 -DA:591,0 -DA:596,1 -DA:597,2 -DA:601,1 -DA:602,1 -DA:603,3 -DA:604,1 -DA:605,1 -DA:606,2 -DA:610,1 -DA:611,2 -DA:618,1 -DA:619,2 -DA:620,1 -DA:621,4 -DA:624,0 -DA:625,0 -DA:629,0 -DA:632,1 -DA:633,2 -DA:634,3 -DA:635,3 -DA:638,1 -DA:642,2 -DA:646,2 -DA:648,1 -DA:651,1 -DA:658,1 -DA:659,4 -DA:662,5 -DA:663,3 -DA:665,5 -DA:666,5 -DA:667,5 -DA:668,5 -DA:677,1 -DA:686,2 -DA:687,1 -DA:688,3 -DA:691,2 -DA:692,3 -DA:693,2 -DA:694,4 -DA:702,3 -DA:713,1 -DA:714,1 -DA:715,1 -DA:718,2 -DA:720,1 -DA:721,1 -DA:730,1 -DA:731,1 -DA:732,1 -DA:734,4 -DA:735,2 -DA:736,1 -DA:739,6 -DA:741,2 -DA:742,0 -DA:743,0 -DA:747,1 -DA:748,2 -DA:751,1 -DA:754,1 -DA:755,4 -DA:762,1 -DA:763,2 -DA:767,2 -DA:768,1 -DA:771,2 -DA:773,4 -LF:245 -LH:236 -end_of_record -SF:lib/src/components/picker/t_picker_items.dart -DA:14,2 -DA:29,2 -DA:39,1 -DA:43,1 -DA:44,1 -DA:50,1 -DA:53,1 -DA:54,3 -DA:55,3 -DA:58,1 -DA:63,3 -DA:66,3 -DA:67,3 -DA:74,1 -DA:75,2 -DA:93,1 -DA:103,1 -DA:107,1 -DA:108,1 -DA:118,1 -DA:121,1 -DA:122,3 -DA:123,3 -DA:130,1 -DA:135,3 -DA:138,2 -DA:139,2 -DA:140,2 -DA:141,5 -DA:142,5 -DA:149,1 -DA:153,0 -DA:154,0 -DA:156,0 -DA:157,0 -DA:159,0 -DA:162,1 -DA:163,2 -DA:164,2 -DA:165,5 -LF:40 -LH:35 -end_of_record -SF:lib/src/components/picker/t_picker_keys.dart -DA:13,2 -DA:35,1 -DA:38,1 -DA:39,3 -DA:40,3 -DA:41,3 -DA:42,3 -DA:43,3 -DA:45,1 -DA:46,5 -DA:48,1 -DA:50,5 -LF:12 -LH:12 -end_of_record -SF:lib/src/components/picker/t_picker_load_event.dart -DA:8,2 -DA:29,1 -DA:30,1 -DA:31,2 -DA:32,2 -LF:5 -LH:5 -end_of_record -SF:lib/src/components/popover/t_popover.dart -DA:6,0 -DA:24,0 -DA:29,0 -LF:3 -LH:0 -end_of_record -SF:lib/src/components/popover/t_popover_widget.dart -DA:66,0 -DA:126,0 -DA:127,0 -DA:135,0 -DA:137,0 -DA:138,0 -DA:139,0 -DA:140,0 -DA:141,0 -DA:144,0 -DA:145,0 -DA:152,0 -DA:153,0 -DA:154,0 -DA:155,0 -DA:159,0 -DA:160,0 -DA:161,0 -DA:164,0 -DA:165,0 -DA:169,0 -DA:170,0 -DA:171,0 -DA:172,0 -DA:173,0 -DA:174,0 -DA:175,0 -DA:178,0 -DA:179,0 -DA:183,0 -DA:184,0 -DA:188,0 -DA:189,0 -DA:190,0 -DA:191,0 -DA:192,0 -DA:193,0 -DA:197,0 -DA:198,0 -DA:202,0 -DA:203,0 -DA:204,0 -DA:207,0 -DA:208,0 -DA:209,0 -DA:210,0 -DA:211,0 -DA:212,0 -DA:216,0 -DA:217,0 -DA:221,0 -DA:222,0 -DA:223,0 -DA:227,0 -DA:230,0 -DA:235,0 -DA:236,0 -DA:237,0 -DA:238,0 -DA:239,0 -DA:241,0 -DA:242,0 -DA:243,0 -DA:245,0 +DA:53,0 +DA:225,0 +DA:226,0 +DA:244,0 DA:246,0 -DA:247,0 +DA:248,0 DA:249,0 -DA:250,0 -DA:251,0 -DA:253,0 +DA:252,0 DA:254,0 DA:255,0 -DA:258,0 +DA:257,0 DA:259,0 -DA:265,0 +DA:260,0 +DA:264,0 DA:266,0 DA:267,0 DA:271,0 @@ -8911,7 +8539,6 @@ DA:279,0 DA:280,0 DA:281,0 DA:282,0 -DA:283,0 DA:284,0 DA:285,0 DA:286,0 @@ -8919,243 +8546,434 @@ DA:287,0 DA:288,0 DA:289,0 DA:290,0 -DA:291,0 -DA:293,0 DA:294,0 DA:295,0 -DA:296,0 DA:297,0 DA:298,0 DA:300,0 -DA:305,0 -DA:306,0 +DA:302,0 +DA:303,0 DA:307,0 -DA:308,0 -DA:309,0 -DA:310,0 DA:311,0 DA:312,0 DA:313,0 -DA:314,0 -DA:315,0 DA:317,0 DA:318,0 -DA:319,0 -DA:320,0 DA:321,0 DA:322,0 -DA:323,0 -DA:324,0 DA:325,0 DA:326,0 -DA:327,0 -DA:329,0 -DA:335,0 +DA:331,0 +DA:333,0 +DA:334,0 DA:336,0 DA:337,0 DA:338,0 -DA:340,0 -DA:342,0 -DA:343,0 DA:344,0 +DA:345,0 DA:346,0 DA:347,0 DA:348,0 +DA:349,0 DA:350,0 -DA:351,0 -DA:353,0 +DA:352,0 DA:354,0 -DA:355,0 +DA:356,0 DA:357,0 +DA:358,0 DA:359,0 +DA:360,0 DA:361,0 DA:362,0 +DA:363,0 DA:364,0 -DA:365,0 -DA:366,0 -DA:368,0 +DA:369,0 DA:370,0 +DA:371,0 DA:372,0 DA:373,0 -DA:375,0 -DA:376,0 -DA:377,0 -DA:380,0 -DA:382,0 -DA:384,0 +DA:374,0 +DA:386,0 +DA:387,0 +DA:388,0 DA:389,0 -DA:390,0 DA:391,0 +DA:392,0 DA:393,0 +DA:394,0 +DA:395,0 +DA:396,0 DA:397,0 DA:398,0 DA:399,0 -DA:400,0 DA:401,0 DA:402,0 +DA:407,0 DA:408,0 DA:410,0 DA:411,0 DA:412,0 DA:413,0 +DA:414,0 DA:416,0 DA:417,0 -DA:418,0 -DA:419,0 -DA:420,0 -DA:421,0 -DA:422,0 -DA:423,0 -DA:424,0 -DA:442,0 -DA:443,0 -DA:444,0 +DA:429,0 +DA:430,0 +DA:431,0 +DA:433,0 +DA:434,0 +DA:435,0 +DA:436,0 +DA:438,0 DA:445,0 DA:446,0 +DA:447,0 +DA:451,0 +DA:452,0 +DA:454,0 DA:455,0 DA:456,0 DA:457,0 DA:458,0 DA:459,0 DA:460,0 -DA:467,0 +DA:462,0 +DA:463,0 +DA:466,0 DA:468,0 DA:469,0 DA:470,0 +DA:471,0 +DA:472,0 DA:473,0 -DA:474,0 -DA:475,0 +DA:476,0 +DA:477,0 DA:478,0 DA:479,0 -DA:480,0 -DA:481,0 -DA:483,0 -DA:492,0 -DA:493,0 -DA:494,0 -DA:497,0 -DA:498,0 +DA:488,0 +DA:491,0 +DA:495,0 +DA:496,0 +DA:499,0 +DA:500,0 DA:501,0 -DA:502,0 DA:505,0 -DA:506,0 -DA:514,0 -DA:515,0 +DA:507,0 +DA:512,0 DA:516,0 -DA:517,0 -DA:518,0 -DA:519,0 -DA:520,0 +DA:521,0 +DA:523,0 +DA:524,0 DA:527,0 -DA:534,0 +DA:530,0 +DA:531,0 +DA:532,0 DA:536,0 DA:537,0 -DA:538,0 -DA:539,0 -DA:540,0 DA:541,0 -DA:542,0 -DA:545,0 -LF:231 +DA:543,0 +DA:546,0 +DA:547,0 +DA:548,0 +DA:549,0 +DA:551,0 +DA:552,0 +DA:553,0 +DA:558,0 +DA:559,0 +DA:563,0 +DA:564,0 +DA:565,0 +DA:566,0 +DA:567,0 +DA:568,0 +DA:572,0 +DA:573,0 +DA:580,0 +DA:581,0 +DA:582,0 +DA:583,0 +DA:586,0 +DA:587,0 +DA:591,0 +DA:594,0 +DA:595,0 +DA:596,0 +DA:597,0 +DA:600,0 +DA:604,0 +DA:608,0 +DA:610,0 +DA:613,0 +DA:620,0 +DA:621,0 +DA:624,0 +DA:625,0 +DA:627,0 +DA:628,0 +DA:629,0 +DA:630,0 +DA:639,0 +DA:648,0 +DA:649,0 +DA:650,0 +DA:653,0 +DA:654,0 +DA:655,0 +DA:656,0 +DA:664,0 +DA:675,0 +DA:676,0 +DA:677,0 +DA:680,0 +DA:682,0 +DA:683,0 +DA:692,0 +DA:693,0 +DA:694,0 +DA:696,0 +DA:697,0 +DA:698,0 +DA:701,0 +DA:703,0 +DA:704,0 +DA:705,0 +DA:709,0 +DA:710,0 +DA:713,0 +DA:716,0 +DA:717,0 +DA:724,0 +DA:725,0 +DA:729,0 +DA:730,0 +DA:733,0 +DA:735,0 +LF:235 LH:0 end_of_record -SF:lib/src/components/popup/t_popup_panel.dart -DA:11,0 -DA:21,0 -DA:70,0 -DA:72,0 -DA:73,0 -DA:77,0 -DA:78,0 -DA:80,0 -DA:88,0 -DA:90,0 -DA:94,0 -DA:95,0 -DA:99,0 -DA:100,0 +SF:lib/src/util/t_toolbar_pressable.dart +DA:10,3 +DA:41,3 +DA:42,3 +DA:48,2 +DA:49,8 +DA:52,4 +DA:55,6 +DA:58,3 +DA:60,3 +DA:61,6 +DA:62,3 +DA:63,3 +DA:64,3 +DA:67,6 +DA:68,6 +DA:69,1 +DA:70,2 +DA:74,6 +DA:75,1 +DA:76,2 +DA:81,12 +DA:83,3 +DA:85,4 +DA:86,4 +DA:87,0 +DA:88,6 +DA:89,3 +DA:90,6 +DA:91,3 +DA:92,3 +LF:30 +LH:29 +end_of_record +SF:lib/src/components/picker/t_picker_items.dart +DA:14,0 +DA:29,0 +DA:39,0 +DA:43,0 +DA:44,0 +DA:50,0 +DA:53,0 +DA:54,0 +DA:55,0 +DA:58,0 +DA:63,0 +DA:66,0 +DA:67,0 +DA:74,0 +DA:75,0 +DA:93,0 DA:103,0 -DA:104,0 -DA:105,0 -DA:106,0 DA:107,0 -DA:110,0 -DA:111,0 -DA:112,0 -DA:113,0 -DA:114,0 -DA:116,0 -DA:117,0 -DA:120,0 +DA:108,0 +DA:118,0 DA:121,0 -DA:124,0 -DA:125,0 +DA:122,0 +DA:123,0 +DA:130,0 +DA:135,0 +DA:138,0 +DA:139,0 +DA:140,0 +DA:141,0 +DA:142,0 +DA:149,0 +DA:153,0 +DA:154,0 +DA:156,0 +DA:157,0 +DA:159,0 +DA:162,0 +DA:163,0 +DA:164,0 +DA:165,0 +LF:40 +LH:0 +end_of_record +SF:lib/src/components/picker/t_picker_keys.dart +DA:13,5 +DA:35,0 +DA:38,0 +DA:39,0 +DA:40,0 +DA:41,0 +DA:42,0 +DA:43,0 +DA:45,0 +DA:46,0 +DA:48,0 +DA:50,0 +LF:12 +LH:1 +end_of_record +SF:lib/src/components/picker/t_picker_normalize.dart +DA:9,0 +DA:12,0 +DA:14,0 +DA:17,0 +DA:18,0 +DA:19,0 +DA:20,0 +DA:22,0 +DA:27,0 +DA:29,0 +DA:32,0 +DA:35,0 +DA:36,0 +DA:39,0 +DA:40,0 +DA:41,0 +DA:42,0 +DA:43,0 +DA:44,0 +DA:46,0 +DA:49,0 +DA:51,0 +DA:52,0 +DA:53,0 +DA:54,0 +DA:59,0 +DA:60,0 +DA:61,0 +DA:63,0 +DA:64,0 +LF:30 +LH:0 +end_of_record +SF:lib/src/components/picker/t_picker_load_event.dart +DA:8,0 +DA:29,0 +DA:30,0 +DA:31,0 +DA:32,0 +LF:5 +LH:0 +end_of_record +SF:lib/src/components/popover/t_popover.dart +DA:6,0 +DA:24,0 +DA:29,0 +LF:3 +LH:0 +end_of_record +SF:lib/src/components/popover/t_popover_widget.dart +DA:66,0 DA:126,0 DA:127,0 -DA:131,0 +DA:135,0 +DA:137,0 +DA:138,0 +DA:139,0 DA:140,0 DA:141,0 DA:144,0 DA:145,0 -DA:146,0 -DA:149,0 -DA:150,0 +DA:152,0 +DA:153,0 DA:154,0 DA:155,0 -DA:156,0 -DA:157,0 -DA:158,0 +DA:159,0 +DA:160,0 DA:161,0 -DA:168,0 +DA:164,0 +DA:165,0 DA:169,0 +DA:170,0 +DA:171,0 DA:172,0 +DA:173,0 DA:174,0 DA:175,0 -DA:176,0 -DA:177,0 -DA:181,0 +DA:178,0 +DA:179,0 +DA:183,0 DA:184,0 -DA:185,0 +DA:188,0 DA:189,0 +DA:190,0 DA:191,0 DA:192,0 DA:193,0 -DA:194,0 DA:197,0 -DA:200,0 -DA:201,0 +DA:198,0 DA:202,0 +DA:203,0 +DA:204,0 +DA:207,0 +DA:208,0 DA:209,0 +DA:210,0 DA:211,0 -DA:213,0 -DA:214,0 -DA:218,0 -DA:219,0 -DA:220,0 +DA:212,0 +DA:216,0 +DA:217,0 DA:221,0 DA:222,0 DA:223,0 -DA:225,0 -DA:226,0 -DA:228,0 -DA:229,0 +DA:227,0 DA:230,0 -DA:232,0 -DA:233,0 -DA:234,0 DA:235,0 DA:236,0 -DA:244,0 +DA:237,0 +DA:238,0 +DA:239,0 +DA:241,0 +DA:242,0 +DA:243,0 DA:245,0 DA:246,0 DA:247,0 -DA:248,0 DA:249,0 DA:250,0 DA:251,0 -DA:256,0 -DA:257,0 +DA:253,0 +DA:254,0 +DA:255,0 DA:258,0 +DA:259,0 +DA:265,0 +DA:266,0 +DA:267,0 DA:271,0 DA:272,0 DA:273,0 @@ -9163,130 +8981,544 @@ DA:277,0 DA:278,0 DA:279,0 DA:280,0 +DA:281,0 +DA:282,0 DA:283,0 DA:284,0 DA:285,0 +DA:286,0 DA:287,0 DA:288,0 DA:289,0 DA:290,0 -DA:292,0 +DA:291,0 +DA:293,0 +DA:294,0 +DA:295,0 +DA:296,0 +DA:297,0 DA:298,0 -DA:334,0 +DA:300,0 +DA:305,0 +DA:306,0 +DA:307,0 +DA:308,0 +DA:309,0 +DA:310,0 +DA:311,0 +DA:312,0 +DA:313,0 +DA:314,0 +DA:315,0 +DA:317,0 +DA:318,0 +DA:319,0 +DA:320,0 +DA:321,0 +DA:322,0 +DA:323,0 +DA:324,0 +DA:325,0 +DA:326,0 +DA:327,0 +DA:329,0 DA:335,0 +DA:336,0 +DA:337,0 +DA:338,0 DA:340,0 DA:342,0 DA:343,0 -DA:345,0 +DA:344,0 DA:346,0 DA:347,0 DA:348,0 -DA:349,0 DA:350,0 +DA:351,0 +DA:353,0 +DA:354,0 +DA:355,0 DA:357,0 -DA:358,0 -DA:360,0 +DA:359,0 DA:361,0 -DA:363,0 +DA:362,0 +DA:364,0 +DA:365,0 DA:366,0 DA:368,0 -DA:369,0 -DA:371,0 +DA:370,0 DA:372,0 -DA:374,0 -DA:381,0 +DA:373,0 +DA:375,0 +DA:376,0 +DA:377,0 +DA:380,0 DA:382,0 -DA:383,0 +DA:384,0 +DA:389,0 DA:390,0 -DA:392,0 -DA:394,0 -DA:395,0 -DA:396,0 +DA:391,0 +DA:393,0 DA:397,0 DA:398,0 +DA:399,0 +DA:400,0 +DA:401,0 DA:402,0 -DA:404,0 -DA:409,0 +DA:408,0 +DA:410,0 +DA:411,0 +DA:412,0 +DA:413,0 +DA:416,0 +DA:417,0 +DA:418,0 +DA:419,0 +DA:420,0 +DA:421,0 +DA:422,0 +DA:423,0 +DA:424,0 +DA:442,0 +DA:443,0 +DA:444,0 +DA:445,0 +DA:446,0 +DA:455,0 +DA:456,0 DA:457,0 DA:458,0 -DA:463,0 -DA:465,0 -DA:466,0 +DA:459,0 +DA:460,0 DA:467,0 +DA:468,0 +DA:469,0 DA:470,0 -DA:472,0 DA:473,0 DA:474,0 -DA:476,0 -DA:477,0 +DA:475,0 +DA:478,0 +DA:479,0 DA:480,0 DA:481,0 -DA:482,0 DA:483,0 -DA:485,0 -DA:486,0 -DA:487,0 -DA:488,0 -DA:495,0 -DA:496,0 +DA:492,0 +DA:493,0 +DA:494,0 +DA:497,0 DA:498,0 -DA:499,0 -DA:507,0 -DA:513,0 +DA:501,0 +DA:502,0 +DA:505,0 +DA:506,0 +DA:514,0 DA:515,0 DA:516,0 +DA:517,0 +DA:518,0 +DA:519,0 DA:520,0 -DA:524,0 -DA:525,0 -DA:526,0 DA:527,0 -DA:528,0 -DA:529,0 -DA:530,0 +DA:534,0 DA:536,0 +DA:537,0 DA:538,0 +DA:539,0 +DA:540,0 DA:541,0 DA:542,0 -DA:543,0 -DA:544,0 DA:545,0 -DA:549,0 -DA:551,0 -DA:556,0 -DA:588,0 -DA:590,0 -DA:591,0 -DA:593,0 -DA:595,0 -DA:597,0 -DA:598,0 -DA:599,0 -DA:600,0 -DA:602,0 -DA:604,0 -DA:605,0 -DA:607,0 -DA:608,0 -DA:610,0 -DA:616,0 -DA:617,0 -DA:618,0 -DA:619,0 -DA:620,0 -DA:622,0 -DA:623,0 -DA:624,0 -DA:625,0 -DA:626,0 -DA:627,0 -DA:628,0 -DA:629,0 -DA:631,0 -DA:632,0 -DA:634,0 -LF:224 +LF:231 LH:0 end_of_record +SF:lib/src/components/popup/_popup_center_close.dart +DA:11,2 +DA:16,4 +DA:17,2 +DA:18,2 +DA:19,4 +DA:20,2 +DA:22,2 +DA:28,4 +DA:33,2 +DA:44,2 +DA:46,2 +DA:47,4 +DA:48,2 +DA:49,4 +DA:50,4 +DA:51,2 +DA:55,2 +DA:56,6 +DA:57,4 +DA:60,2 +DA:62,2 +DA:66,2 +DA:69,2 +LF:23 +LH:23 +end_of_record +SF:lib/src/components/popup/t_popup_types.dart +DA:26,5 +DA:28,1 +DA:37,3 +DA:68,1 +DA:73,4 +DA:74,4 +DA:86,1 +DA:91,3 +DA:92,3 +LF:9 +LH:9 +end_of_record +SF:lib/src/components/popup/_popup_header.dart +DA:15,3 +DA:26,3 +DA:28,15 +DA:32,6 +DA:33,6 +DA:35,2 +DA:39,6 +DA:40,3 +DA:42,3 +DA:43,3 +DA:44,3 +DA:49,3 +DA:54,2 +DA:56,2 +DA:57,4 +DA:66,2 +DA:67,2 +DA:68,2 +DA:69,2 +DA:70,4 +DA:71,2 +DA:73,4 +DA:74,2 +DA:76,4 +DA:77,4 +DA:81,3 +DA:82,6 +DA:83,2 +DA:85,12 +DA:86,2 +DA:87,4 +DA:88,4 +DA:89,4 +DA:98,2 +DA:99,4 +DA:100,3 +DA:102,6 +DA:103,1 +DA:104,4 +DA:105,1 +DA:106,1 +DA:109,2 +DA:112,2 +DA:113,4 +DA:114,3 +DA:116,6 +DA:117,1 +DA:118,4 +DA:119,1 +DA:120,1 +DA:124,2 +DA:129,3 +DA:137,3 +DA:139,3 +DA:140,6 +DA:141,3 +DA:142,6 +DA:143,3 +DA:144,3 +DA:150,3 +DA:151,3 +DA:152,6 +DA:153,3 +DA:154,6 +DA:155,3 +DA:157,6 +DA:159,3 +DA:160,2 +DA:161,5 +DA:162,4 +DA:163,4 +DA:166,3 +DA:171,2 +DA:172,6 +DA:173,6 +DA:174,3 +DA:175,6 +DA:176,3 +DA:178,6 +DA:180,3 +DA:181,1 +DA:182,3 +DA:183,2 +DA:184,2 +DA:187,3 +DA:192,2 +DA:197,3 +DA:198,6 +DA:199,3 +DA:201,9 +DA:202,3 +DA:203,12 +DA:204,3 +DA:205,3 +DA:208,4 +DA:211,3 +DA:212,6 +DA:213,3 +DA:215,9 +DA:216,3 +DA:217,12 +DA:218,3 +DA:219,3 +DA:223,4 +DA:227,3 +DA:228,3 +DA:229,1 +DA:232,6 +DA:235,3 +DA:236,3 +DA:237,1 +DA:240,6 +LF:112 +LH:112 +end_of_record +SF:lib/src/components/popup/_popup_layout.dart +DA:7,4 +DA:26,4 +DA:27,8 +DA:30,4 +DA:33,4 +DA:34,4 +DA:35,4 +DA:36,4 +DA:37,3 +DA:38,3 +DA:39,3 +DA:40,3 +DA:41,3 +DA:44,4 +DA:45,4 +DA:46,8 +DA:47,2 +DA:48,2 +DA:49,2 +DA:50,2 +DA:56,3 +DA:57,3 +DA:58,3 +DA:59,3 +DA:64,1 +DA:65,2 +DA:66,1 +DA:67,1 +DA:68,1 +DA:71,3 +DA:72,3 +DA:73,3 +DA:74,3 +DA:75,3 +DA:76,3 +DA:79,3 +DA:80,3 +DA:81,3 +DA:82,3 +DA:83,3 +DA:84,3 +DA:87,3 +DA:88,3 +DA:89,3 +DA:90,3 +DA:91,6 +DA:92,6 +DA:100,4 +DA:101,4 +DA:102,4 +DA:104,6 +DA:105,12 +DA:110,2 +DA:111,2 +DA:112,2 +DA:114,2 +DA:116,2 +DA:118,2 +DA:120,1 +DA:125,4 +DA:126,4 +DA:127,4 +DA:128,6 +DA:129,4 +DA:130,8 +DA:131,2 +DA:132,4 +DA:133,2 +DA:134,4 +DA:135,1 +LF:70 +LH:70 +end_of_record +SF:lib/src/components/popup/_popup_route.dart +DA:10,3 +DA:13,3 +DA:14,3 +DA:16,3 +DA:17,3 +DA:18,3 +DA:32,3 +DA:33,6 +DA:36,6 +DA:37,6 +DA:38,3 +DA:39,3 +DA:44,3 +DA:45,6 +DA:47,3 +DA:48,6 +DA:50,3 +DA:53,3 +DA:55,9 +DA:57,3 +DA:63,3 +DA:66,3 +DA:67,6 +DA:70,3 +DA:71,3 +DA:74,3 +DA:75,8 +DA:76,8 +DA:79,1 +DA:88,3 +DA:95,3 +DA:101,3 +DA:102,6 +DA:103,3 +DA:104,6 +DA:106,6 +DA:107,6 +DA:108,3 +DA:109,6 +DA:110,6 +DA:111,6 +DA:113,9 +DA:114,4 +DA:117,3 +DA:118,3 +DA:119,3 +DA:120,3 +DA:124,9 +DA:125,2 +DA:131,3 +DA:132,6 +DA:137,6 +DA:139,3 +DA:141,3 +DA:143,3 +DA:144,9 +DA:145,12 +DA:146,3 +DA:147,3 +DA:152,3 +DA:153,3 +DA:155,3 +DA:156,3 +DA:157,6 +DA:158,9 +DA:162,6 +DA:163,3 +DA:164,3 +DA:169,6 +DA:170,3 +DA:175,3 +DA:176,3 +DA:177,1 +DA:182,2 +DA:183,5 +DA:184,4 +DA:185,4 +DA:189,3 +DA:190,6 +DA:191,3 +DA:192,7 +DA:194,6 +DA:195,3 +DA:196,7 +DA:200,3 +DA:201,3 +DA:204,3 +DA:208,3 +DA:209,6 +DA:210,6 +DA:211,3 +DA:215,3 +DA:217,7 +DA:218,8 +DA:219,3 +DA:220,6 +DA:224,3 +DA:226,3 +DA:227,3 +DA:230,3 +DA:232,3 +DA:233,9 +DA:235,3 +LF:103 +LH:103 +end_of_record +SF:lib/src/components/popup/_popup_shell.dart +DA:13,3 +DA:22,3 +DA:24,3 +DA:25,9 +DA:27,9 +DA:28,9 +DA:30,6 +DA:32,9 +DA:33,4 +DA:34,2 +DA:35,2 +DA:37,2 +DA:42,2 +DA:43,2 +DA:45,2 +DA:48,2 +DA:49,2 +DA:50,4 +DA:51,4 +DA:52,2 +DA:53,2 +DA:55,2 +DA:64,9 +DA:65,9 +DA:66,6 +DA:68,3 +DA:69,3 +DA:74,9 +DA:75,3 +DA:79,3 +DA:80,3 +DA:81,3 +DA:82,3 +DA:84,5 +DA:88,1 +DA:91,2 +DA:99,3 +DA:101,3 +DA:102,4 +DA:103,3 +DA:104,6 +DA:105,2 +DA:106,2 +DA:107,2 +DA:108,2 +DA:109,2 +DA:110,2 +LF:47 +LH:47 +end_of_record SF:lib/src/components/progress/t_progress.dart DA:13,0 DA:17,0 @@ -10653,7 +10885,7 @@ LF:90 LH:0 end_of_record SF:lib/src/components/skeleton/t_skeleton_rowcol.dart -DA:7,1 +DA:7,5 DA:15,0 DA:16,0 DA:21,0 @@ -10667,11 +10899,11 @@ DA:41,0 DA:42,0 DA:43,0 DA:44,0 -DA:50,1 -DA:56,1 -DA:60,1 -DA:64,1 -DA:68,1 +DA:50,5 +DA:56,5 +DA:60,5 +DA:64,5 +DA:68,5 DA:79,0 DA:80,0 DA:83,0 @@ -10682,10 +10914,10 @@ DA:92,0 DA:95,0 DA:96,0 DA:101,0 -DA:110,1 -DA:119,1 -DA:128,1 -DA:137,1 +DA:110,5 +DA:119,5 +DA:128,5 +DA:137,5 DA:160,0 LF:34 LH:10 @@ -11044,7 +11276,7 @@ DA:523,0 DA:524,0 DA:525,0 DA:526,0 -DA:532,1 +DA:532,5 DA:534,0 DA:539,0 DA:561,0 @@ -12313,17 +12545,17 @@ LF:40 LH:0 end_of_record SF:lib/src/theme/basic.dart -DA:9,1 -DA:10,2 -DA:11,3 -DA:14,1 -DA:15,4 -DA:17,1 -DA:18,1 -DA:19,2 -DA:28,1 -DA:30,1 -DA:31,3 +DA:9,3 +DA:10,6 +DA:11,9 +DA:14,3 +DA:15,12 +DA:17,3 +DA:18,3 +DA:19,6 +DA:28,3 +DA:30,3 +DA:31,9 DA:36,0 DA:38,0 DA:39,0 @@ -12361,7 +12593,7 @@ DA:50,0 DA:52,0 DA:53,0 DA:54,0 -DA:88,1 +DA:88,5 DA:99,0 DA:101,0 DA:102,0 @@ -12402,7 +12634,7 @@ DA:158,0 DA:163,0 DA:164,0 DA:166,0 -DA:199,1 +DA:199,5 DA:211,0 DA:213,0 DA:214,0 @@ -12416,7 +12648,7 @@ DA:223,0 DA:225,0 DA:226,0 DA:227,0 -DA:251,1 +DA:251,5 DA:259,0 DA:261,0 DA:262,0 @@ -15477,19 +15709,19 @@ LF:176 LH:0 end_of_record SF:lib/src/theme/resource_delegate.dart -DA:20,0 -DA:21,0 +DA:20,3 +DA:21,3 DA:22,0 -DA:24,0 -DA:26,0 +DA:24,3 +DA:26,6 DA:31,0 DA:32,0 -DA:38,0 -DA:39,0 +DA:38,3 +DA:39,3 DA:44,0 -DA:47,0 -DA:48,0 -DA:49,0 +DA:47,3 +DA:48,3 +DA:49,3 DA:212,0 DA:215,0 DA:218,0 @@ -15542,7 +15774,7 @@ DA:356,0 DA:359,0 DA:362,0 LF:64 -LH:0 +LH:9 end_of_record SF:lib/src/theme/t_shadows.dart DA:7,0 @@ -15564,17 +15796,17 @@ LF:8 LH:0 end_of_record SF:lib/src/util/string_util.dart -DA:5,1 -DA:7,2 -DA:9,2 +DA:5,3 +DA:7,6 +DA:9,6 DA:14,0 -DA:17,2 -DA:18,1 -DA:20,1 -DA:23,2 -DA:24,1 -DA:27,1 -DA:30,2 +DA:17,6 +DA:18,3 +DA:20,3 +DA:23,6 +DA:24,3 +DA:27,3 +DA:30,6 LF:11 LH:10 end_of_record diff --git a/tdesign-component/demo_tool/all_build.sh b/tdesign-component/demo_tool/all_build.sh index 0a881ce83..1a8f8977d 100644 --- a/tdesign-component/demo_tool/all_build.sh +++ b/tdesign-component/demo_tool/all_build.sh @@ -129,7 +129,7 @@ dart run tdesign_flutter_tools:main generate --folder "$PARENT_DIR/lib/src/compo # popover dart run tdesign_flutter_tools:main generate --folder "$PARENT_DIR/lib/src/components/popover" --name TPopover,TPopoverWidget --folder-name popover --output "$PARENT_DIR/example/assets/api/" --only-api --get-comments # popup -dart run tdesign_flutter_tools:main generate --folder "$PARENT_DIR/lib/src/components/popup" --name TSlidePopupRoute,TPopupBottomDisplayPanel,TPopupBottomConfirmPanel,TPopupCenterPanel --folder-name popup --output "$PARENT_DIR/example/assets/api/" --only-api --get-comments +dart run tdesign_flutter_tools:main generate --folder "$PARENT_DIR/lib/src/components/popup" --name TPopup,TPopupOptions,TPopupHandle,TPopupPlacement,TPopupTrigger --folder-name popup --output "$PARENT_DIR/example/assets/api/" --only-api --get-comments # refresh dart run tdesign_flutter_tools:main generate --file "$PARENT_DIR/lib/src/components/refresh/t_refresh_header.dart" --name TRefreshHeader --folder-name pull-down-refresh --output "$PARENT_DIR/example/assets/api/" --only-api --get-comments # swipecell diff --git a/tdesign-component/example/assets/api/action-sheet_api.md b/tdesign-component/example/assets/api/action-sheet_api.md index a08aa51e4..64bc4b9dd 100644 --- a/tdesign-component/example/assets/api/action-sheet_api.md +++ b/tdesign-component/example/assets/api/action-sheet_api.md @@ -9,14 +9,12 @@ | badge | TBadge? | - | 角标 | | description | String? | - | 描述信息 | | disabled | bool | false | 是否禁用 | -| group | String? | - | 分组,用于带描述多行滚动宫格 | +| group | String? | - | 分组,用于带描述多行滚动宫格 当[TActionSheet.theme]等于[TActionSheetTheme.group]时有效 有效时,如果该值未配置整个[TActionSheetItem]会被忽略,即不会展示 | | icon | Widget? | - | 图标 | | iconSize | double? | - | 图标大小 | | label | String | - | 标题 | | textStyle | TextStyle? | - | 标题样式 | -``` -``` ### TActionSheet #### 简介 @@ -25,23 +23,23 @@ | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | +| context | BuildContext | - | 上下文 | | align | TActionSheetAlign | TActionSheetAlign.center | 对齐方式 | | cancelText | String? | - | 取消按钮的文本 | | closeOnOverlayClick | bool | true | 点击蒙层时是否关闭 | -| context | BuildContext | context | 上下文 | -| count | int | 8 | 每页显示的项目数 | -| description | String? | - | 描述文本 | -| itemHeight | double | 96.0 | 项目的行高 | -| itemMinWidth | double | 80.0 | 项目的最小宽度 | +| count | int | 8 | 每页显示的项目数 当[theme]等于[TActionSheetTheme.grid]且[showPagination]为true时有效 | +| description | String? | - | 描述文本 当[theme]等于[TActionSheetTheme.grid]或[theme]等于[TActionSheetTheme.list]时有效 | +| itemHeight | double | 96.0 | 项目的行高 当[theme]等于[TActionSheetTheme.grid]或[theme]等于[TActionSheetTheme.group]时有效 | +| itemMinWidth | double | 80.0 | 项目的最小宽度 当[theme]等于[TActionSheetTheme.grid]且[scrollable]为true时有效 或当[theme]等于[TActionSheetTheme.group]时有效 | | items | List | - | ActionSheet的项目列表 | | onCancel | VoidCallback? | - | 取消按钮的回调函数 | | onClose | VoidCallback? | - | 关闭时的回调函数 | | onSelected | TActionSheetItemCallback? | - | 选择项目时的回调函数 | -| rows | int | 2 | 显示的行数 | -| scrollable | bool | false | 是否可以横向滚动 | +| rows | int | 2 | 显示的行数 当[theme]等于[TActionSheetTheme.grid]时有效 | +| scrollable | bool | false | 是否可以横向滚动 当[theme]等于[TActionSheetTheme.grid]且[showPagination]为false时有效 | | showCancel | bool | true | 是否显示取消按钮 | | showOverlay | bool | true | 是否显示遮罩层 | -| showPagination | bool | false | 是否显示分页 | +| showPagination | bool | false | 是否显示分页 当[theme]等于[TActionSheetTheme.grid]时有效 | | theme | TActionSheetTheme | TActionSheetTheme.list | 主题样式 | | useSafeArea | bool | true | 使用安全区域 | | visible | bool | false | 是否立即显示 | @@ -49,8 +47,103 @@ #### 静态方法 -| 名称 | 返回类型 | 参数 | 说明 | +##### TActionSheet.showGridActionSheet + +显示宫格类型面板 + +返回类型:`void` + +| 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | -| showGridActionSheet | | required BuildContext context, required List items, TActionSheetAlign align, String? cancelText, bool showCancel, TActionSheetItemCallback? onSelected, bool showOverlay, bool closeOnOverlayClick, int count, int rows, double itemHeight, double itemMinWidth, bool scrollable, bool showPagination, VoidCallback? onCancel, String? description, VoidCallback? onClose, bool useSafeArea, | 显示宫格类型面板 | -| showGroupActionSheet | | required BuildContext context, required List items, TActionSheetAlign align, String? cancelText, bool showCancel, TActionSheetItemCallback? onSelected, bool showOverlay, bool closeOnOverlayClick, double itemHeight, double itemMinWidth, VoidCallback? onCancel, VoidCallback? onClose, bool useSafeArea, | 显示分组类型面板 | -| showListActionSheet | | required BuildContext context, required List items, TActionSheetAlign align, String? cancelText, bool showCancel, VoidCallback? onCancel, TActionSheetItemCallback? onSelected, bool showOverlay, bool closeOnOverlayClick, VoidCallback? onClose, bool useSafeArea, | 显示列表类型面板 | +| context | BuildContext | - | 上下文 | +| items | List | - | ActionSheet的项目列表 | +| align | TActionSheetAlign | TActionSheetAlign.center | 对齐方式 | +| cancelText | String? | - | 取消按钮的文本 | +| showCancel | bool | true | 是否显示取消按钮 | +| onSelected | TActionSheetItemCallback? | - | 选择项目时的回调函数 | +| showOverlay | bool | true | 是否显示遮罩层 | +| closeOnOverlayClick | bool | true | 点击蒙层时是否关闭 | +| count | int | 8 | 每页显示的项目数 当[theme]等于[TActionSheetTheme.grid]且[showPagination]为true时有效 | +| rows | int | 2 | 显示的行数 当[theme]等于[TActionSheetTheme.grid]时有效 | +| itemHeight | double | 96.0 | 项目的行高 当[theme]等于[TActionSheetTheme.grid]或[theme]等于[TActionSheetTheme.group]时有效 | +| itemMinWidth | double | 80.0 | 项目的最小宽度 当[theme]等于[TActionSheetTheme.grid]且[scrollable]为true时有效 或当[theme]等于[TActionSheetTheme.group]时有效 | +| scrollable | bool | false | 是否可以横向滚动 当[theme]等于[TActionSheetTheme.grid]且[showPagination]为false时有效 | +| showPagination | bool | false | 是否显示分页 当[theme]等于[TActionSheetTheme.grid]时有效 | +| onCancel | VoidCallback? | - | 取消按钮的回调函数 | +| description | String? | - | 描述文本 当[theme]等于[TActionSheetTheme.grid]或[theme]等于[TActionSheetTheme.list]时有效 | +| onClose | VoidCallback? | - | 关闭时的回调函数 | +| useSafeArea | bool | true | 使用安全区域 | + + +##### TActionSheet.showGroupActionSheet + +显示分组类型面板 + +返回类型:`void` + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| context | BuildContext | - | 上下文 | +| items | List | - | ActionSheet的项目列表 | +| align | TActionSheetAlign | TActionSheetAlign.left | 对齐方式 | +| cancelText | String? | - | 取消按钮的文本 | +| showCancel | bool | true | 是否显示取消按钮 | +| onSelected | TActionSheetItemCallback? | - | 选择项目时的回调函数 | +| showOverlay | bool | true | 是否显示遮罩层 | +| closeOnOverlayClick | bool | true | 点击蒙层时是否关闭 | +| itemHeight | double | 96.0 | 项目的行高 当[theme]等于[TActionSheetTheme.grid]或[theme]等于[TActionSheetTheme.group]时有效 | +| itemMinWidth | double | 80.0 | 项目的最小宽度 当[theme]等于[TActionSheetTheme.grid]且[scrollable]为true时有效 或当[theme]等于[TActionSheetTheme.group]时有效 | +| onCancel | VoidCallback? | - | 取消按钮的回调函数 | +| onClose | VoidCallback? | - | 关闭时的回调函数 | +| useSafeArea | bool | true | 使用安全区域 | + + +##### TActionSheet.showListActionSheet + +显示列表类型面板 + +返回类型:`void` + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| context | BuildContext | - | 上下文 | +| items | List | - | ActionSheet的项目列表 | +| align | TActionSheetAlign | TActionSheetAlign.center | 对齐方式 | +| cancelText | String? | - | 取消按钮的文本 | +| showCancel | bool | true | 是否显示取消按钮 | +| onCancel | VoidCallback? | - | 取消按钮的回调函数 | +| onSelected | TActionSheetItemCallback? | - | 选择项目时的回调函数 | +| showOverlay | bool | true | 是否显示遮罩层 | +| closeOnOverlayClick | bool | true | 点击蒙层时是否关闭 | +| onClose | VoidCallback? | - | 关闭时的回调函数 | +| useSafeArea | bool | true | 使用安全区域 | + + +### TActionSheetTheme +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| list | - | +| grid | - | +| group | - | + + +### TActionSheetAlign +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| center | - | +| left | - | +| right | - | + + +### TActionSheetItemCallback +#### 类型定义 + +```dart +typedef TActionSheetItemCallback = void Function(TActionSheetItem item, int index); +``` diff --git a/tdesign-component/example/assets/api/avatar_api.md b/tdesign-component/example/assets/api/avatar_api.md index 9f1dd77b2..ddc9cf598 100644 --- a/tdesign-component/example/assets/api/avatar_api.md +++ b/tdesign-component/example/assets/api/avatar_api.md @@ -15,10 +15,44 @@ | displayText | String? | - | 纯展示类型末尾文字 | | fit | BoxFit? | - | 自定义图片对齐方式 | | icon | IconData? | - | 自定义图标 | -| key | | - | | -| onTap | Function()? | - | 操作点击事件 | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | +| onTap | Function()? | - | 操作点击事件 | | radius | double? | - | 自定义圆角 | | shape | TAvatarShape | TAvatarShape.circle | 头像形状 | | size | TAvatarSize | TAvatarSize.medium | 头像尺寸 | | text | String? | - | 自定义文字 | | type | TAvatarType | TAvatarType.normal | 头像类型 | + + +### TAvatarSize +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| large | - | +| medium | - | +| small | - | + + +### TAvatarType +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| icon | - | +| normal | - | +| customText | - | +| display | - | +| operation | - | + + +### TAvatarShape +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| circle | - | +| square | - | diff --git a/tdesign-component/example/assets/api/back-top_api.md b/tdesign-component/example/assets/api/back-top_api.md index 0eaf4fdd6..8d2feb808 100644 --- a/tdesign-component/example/assets/api/back-top_api.md +++ b/tdesign-component/example/assets/api/back-top_api.md @@ -5,8 +5,28 @@ | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | controller | ScrollController? | - | 页面滚动的控制器 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | onClick | VoidCallback? | - | 按钮点击事件 | | showText | bool | false | 是否展示文字 | | style | TBackTopStyle | TBackTopStyle.circle | 样式,圆形和半圆 | | theme | TBackTopTheme | TBackTopTheme.light | 主题 | + + +### TBackTopTheme +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| light | - | +| dark | - | + + +### TBackTopStyle +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| circle | - | +| halfCircle | - | diff --git a/tdesign-component/example/assets/api/badge_api.md b/tdesign-component/example/assets/api/badge_api.md index 620ac2afa..e91b2fb3a 100644 --- a/tdesign-component/example/assets/api/badge_api.md +++ b/tdesign-component/example/assets/api/badge_api.md @@ -4,16 +4,49 @@ | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | +| type | TBadgeType | - | 红点样式 | | border | TBadgeBorder | TBadgeBorder.large | 红点圆角大小 | | color | Color? | - | 红点颜色 | | count | String? | - | 红点数量 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | maxCount | String? | '99' | 最大红点数量 | | message | String? | - | 消息内容 | | padding | EdgeInsetsGeometry? | - | 角标自定义padding | | showZero | bool | true | 值为0是否显示 | | size | TBadgeSize | TBadgeSize.small | 红点尺寸 | | textColor | Color? | - | 文字颜色 | -| type | TBadgeType | type | 红点样式 | | widthLarge | double | 32 | 角标大三角形宽 | | widthSmall | double | 12 | 角标小三角形宽 | + + +### TBadgeType +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| redPoint | 红点样式 | +| message | 消息样式 | +| bubble | 气泡样式 | +| square | 方形样式 | +| subscript | 角标样式 | + + +### TBadgeBorder +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| large | 大圆角 8px | +| small | 小圆角 2px | + + +### TBadgeSize +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| large | 宽 20px | +| small | 宽 16px | diff --git a/tdesign-component/example/assets/api/button_api.md b/tdesign-component/example/assets/api/button_api.md index be9eb3640..c036c83cb 100644 --- a/tdesign-component/example/assets/api/button_api.md +++ b/tdesign-component/example/assets/api/button_api.md @@ -16,7 +16,7 @@ | iconTextSpacing | double? | - | 自定义图标与文本之间距离 | | iconWidget | Widget? | - | 自定义图标 icon 控件 | | isBlock | bool | false | 是否为通栏按钮 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | margin | EdgeInsetsGeometry? | - | 自定义 margin | | onLongPress | TButtonEvent? | - | 长按事件 | | onTap | TButtonEvent? | - | 点击事件 | @@ -30,8 +30,6 @@ | type | TButtonType | TButtonType.fill | 类型:填充,描边,文字 | | width | double? | - | 自定义宽度 | -``` -``` ### TButtonStyle #### 默认构造方法 @@ -48,9 +46,123 @@ #### 工厂构造方法 -| 名称 | 说明 | -| --- | --- | -| TButtonStyle.generateFillStyleByTheme | 生成不同主题的填充按钮样式 | -| TButtonStyle.generateGhostStyleByTheme | 生成不同主题的幽灵按钮样式 | -| TButtonStyle.generateOutlineStyleByTheme | 生成不同主题的描边按钮样式 | -| TButtonStyle.generateTextStyleByTheme | 生成不同主题的文本按钮样式 | +##### TButtonStyle.generateFillStyleByTheme + +生成不同主题的填充按钮样式 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| context | BuildContext | - | - | +| theme | TButtonTheme? | - | - | +| status | TButtonStatus | - | - | + + +##### TButtonStyle.generateGhostStyleByTheme + +生成不同主题的幽灵按钮样式 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| context | BuildContext | - | - | +| theme | TButtonTheme? | - | - | +| status | TButtonStatus | - | - | + + +##### TButtonStyle.generateOutlineStyleByTheme + +生成不同主题的描边按钮样式 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| context | BuildContext | - | - | +| theme | TButtonTheme? | - | - | +| status | TButtonStatus | - | - | + + +##### TButtonStyle.generateTextStyleByTheme + +生成不同主题的文本按钮样式 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| context | BuildContext | - | - | +| theme | TButtonTheme? | - | - | +| status | TButtonStatus | - | - | + + +### TButtonSize +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| large | - | +| medium | - | +| small | - | +| extraSmall | - | + + +### TButtonType +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| fill | - | +| outline | - | +| text | - | +| ghost | - | + + +### TButtonShape +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| rectangle | - | +| round | - | +| square | - | +| circle | - | +| filled | - | + + +### TButtonTheme +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| defaultTheme | - | +| primary | - | +| danger | - | +| light | - | + + +### TButtonStatus +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| defaultState | - | +| active | - | +| disable | - | + + +### TButtonIconPosition +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| left | - | +| right | - | + + +### TButtonEvent +#### 类型定义 + +```dart +typedef TButtonEvent = void Function(); +``` diff --git a/tdesign-component/example/assets/api/calendar_api.md b/tdesign-component/example/assets/api/calendar_api.md index b5bd37505..76638af64 100644 --- a/tdesign-component/example/assets/api/calendar_api.md +++ b/tdesign-component/example/assets/api/calendar_api.md @@ -15,7 +15,7 @@ | format | CalendarFormat? | - | 用于格式化日期的函数,可定义日期前后的显示内容和日期样式 | | height | double? | - | 高度 | | isTimeUnit | bool? | true | 是否显示时间单位 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | maxDate | int? | - | 最大可选的日期(fromMillisecondsSinceEpoch),不传则默认半年后 | | minDate | int? | - | 最小可选的日期(fromMillisecondsSinceEpoch),不传则默认今天 | | monthTitleBuilder | Widget Function(BuildContext context, DateTime monthDate)? | - | 月标题构建器 | @@ -38,26 +38,22 @@ | value | List? | - | 当前选择的日期(fromMillisecondsSinceEpoch),不传则默认今天,当 type = single 时数组长度为1 | | width | double? | - | 宽度 | -``` -``` ### TCalendarPopup #### 默认构造方法 | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | +| context | BuildContext | - | 上下文 | | autoClose | bool? | true | 自动关闭;在点击关闭按钮、确认按钮、遮罩层时自动关闭 | | builder | CalendarBuilder? | - | 控件构建器,优先级高于[child] | | child | TCalendar? | - | 日历控件 | | confirmBtn | Widget? | - | 自定义确认按钮 | -| context | BuildContext | context | 上下文 | | onClose | VoidCallback? | - | 关闭时触发 | | onConfirm | void Function(List value)? | - | 点击确认按钮时触发 | | top | double? | - | 距离顶部的距离 | | visible | bool? | - | 默认是否显示日历 | -``` -``` ### TCalendarStyle #### 默认构造方法 @@ -69,27 +65,63 @@ | cellStyle | TextStyle? | - | 日期样式 | | cellSuffixStyle | TextStyle? | - | 日期后面的字符串的样式 | | centreColor | Color? | - | 日期范围内背景样式 | -| decoration | | - | | +| decoration | BoxDecoration? | - | - | | monthTitleStyle | TextStyle? | - | body区域 年月文字样式 | | titleCloseColor | Color? | - | header区域 关闭图标的颜色 | | titleMaxLine | int? | - | header区域 [TCalendar.title]的行数 | | titleStyle | TextStyle? | - | header区域 [TCalendar.title]的样式 | | weekdayStyle | TextStyle? | - | header区域 周 文字样式 | +#### 公开属性 + +| 属性 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| bodyPadding | double? | - | 月与月之间的垂直间距 | +| todayStyle | TextStyle? | - | 当天日期样式 | +| verticalGap | double? | - | 日期垂直间距,水平间距为[verticalGap] / 2 | + #### 工厂构造方法 -| 名称 | 说明 | -| --- | --- | -| TCalendarStyle.cellStyle | 日期样式 | -| TCalendarStyle.generateStyle | 生成默认样式 | +##### TCalendarStyle.cellStyle + +日期样式 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| context | BuildContext | - | - | +| type | DateSelectType? | - | - | + + +##### TCalendarStyle.generateStyle + +生成默认样式 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| context | BuildContext | - | - | -``` -``` ### TCalendarDataSource -``` -``` +#### 简介 +日历数据源接口 + + 开发者需要实现此接口来提供农历转换能力。 + 组件内部不包含农历算法和数据,完全依赖外部实现。 + +#### 方法 + +| 名称 | 返回类型 | 参数 | 说明 | +| --- | --- | --- | --- | +| getLunarInfo | TLunarInfo? | required DateTime solarDate | 获取指定阳历日期的农历信息 [solarDate] 阳历日期 返回 null 表示不显示农历信息 | +| formatDate | String | required DateTime date, required TCalendarDateType type, TLunarInfo? lunarInfo | 格式化日期文本 [date] 阳历日期 [type] 日历类型 [lunarInfo] 农历信息(可选) 返回格式化后的日期字符串 | +| getSolarTerm | String? | required DateTime date | 获取节气信息(可选实现) [date] 阳历日期 返回节气名称,如"春分"、"秋分"等,无节气则返回 null | +| getFestival | String? | required DateTime date, TLunarInfo? lunarInfo | 获取节日信息(可选实现) [date] 阳历日期 [lunarInfo] 农历信息(可选) 返回节日名称,如"春节"、"中秋节"等,无节日则返回 null | +| getHolidayInfo | Map? | required DateTime date | 获取假期信息(可选实现) [date] 阳历日期 返回假期类型和名称: - 'holiday': 法定节假日/公共假期(如"国庆节") - 'workday': 调休工作日(如"补班") - null: 正常日期 示例返回值: - {'type': 'holiday', 'name': '国庆节'} - {'type': 'workday', 'name': '补班'} - null | +| formatYear | String | required int year, required TCalendarDateType type | 格式化年份文本 [year] 年份 [type] 日历类型 返回格式化后的年份字符串 阳历示例:2025 -> "2025年" 阴历示例:2025 -> "二〇二五年" | +| formatMonth | String | required int month, required TCalendarDateType type, bool isLeapMonth | 格式化月份文本 [month] 月份(1-12) [type] 日历类型 [isLeapMonth] 是否是闰月(仅农历有效) 返回格式化后的月份字符串 阳历示例:3 -> "3月" 阴历示例:3 -> "三月",闰3月 -> "闰三月" | +| formatDay | String | required int day, required TCalendarDateType type | 格式化日期文本 [day] 日期(1-31) [type] 日历类型 返回格式化后的日期字符串 阳历示例:7 -> "7日" 阴历示例:7 -> "初七" | + ### TLunarInfo #### 默认构造方法 @@ -103,3 +135,67 @@ | monthText | String | - | 月份文本(如:三月、闰三月) | | year | int | - | 农历年份(数字) | | yearText | String | - | 年份文本(如:二〇二五) | + + +### TCalendarDateType +#### 简介 +日历类型枚举 +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| solar | 阳历(公历) | +| lunar | 阴历(农历) | + + +### CalendarType +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| single | - | +| multiple | - | +| range | - | + + +### CalendarTrigger +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| closeBtn | - | +| confirmBtn | - | +| overlay | - | + + +### DateSelectType +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| selected | - | +| disabled | - | +| start | - | +| centre | - | +| end | - | +| empty | - | + + +### CalendarFormat +#### 类型定义 + +```dart +typedef CalendarFormat = TDate? Function(TDate? day); +``` + + +### CalendarBuilder +#### 类型定义 + +```dart +typedef CalendarBuilder = Widget Function(BuildContext context); +``` diff --git a/tdesign-component/example/assets/api/cascader_api.md b/tdesign-component/example/assets/api/cascader_api.md index 7906e3559..bfd99ac6a 100644 --- a/tdesign-component/example/assets/api/cascader_api.md +++ b/tdesign-component/example/assets/api/cascader_api.md @@ -12,7 +12,7 @@ | initialData | String? | - | 初始化数据 | | initialIndexes | List? | - | 若为null表示全部从零开始 | | isLetterSort | bool | false | 是否开启字母排序 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | onChange | MultiCascaderCallback | - | 值发生变更时触发 | | onClose | Function? | - | 选择器关闭按钮回调 | | subTitles | List? | - | 每级展示的次标题 | @@ -20,3 +20,11 @@ | title | String? | - | 选择器标题 | | titleStyle | TextStyle? | - | 标题样式 | | topRadius | double? | - | 顶部圆角 | + + +### MultiCascaderCallback +#### 类型定义 + +```dart +typedef MultiCascaderCallback = void Function(List selected); +``` diff --git a/tdesign-component/example/assets/api/cell_api.md b/tdesign-component/example/assets/api/cell_api.md index b66815628..c7563c928 100644 --- a/tdesign-component/example/assets/api/cell_api.md +++ b/tdesign-component/example/assets/api/cell_api.md @@ -18,7 +18,7 @@ | imageCircle | double? | 50 | 主图圆角,默认50(圆形) | | imageSize | double? | - | 主图尺寸 | | imageWidget | Widget? | - | 主图组件 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | leftIcon | IconData? | - | 左侧图标,出现在单元格标题的左侧 | | leftIconWidget | Widget? | - | 左侧图标组件 | | note | String? | - | 和标题同行的说明文字 | @@ -35,8 +35,6 @@ | title | String? | - | 标题 | | titleWidget | Widget? | - | 标题组件 | -``` -``` ### TCellGroup #### 简介 @@ -49,15 +47,13 @@ | builder | CellBuilder? | - | cell构建器,可自定义cell父组件,如Dismissible | | cells | List | - | 单元格列表 | | isShowLastBordered | bool? | false | 是否显示最后一个cell的下边框 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | scrollable | bool? | false | 可滚动 | | style | TCellStyle? | - | 自定义样式 | | theme | TCellGroupTheme? | TCellGroupTheme.defaultTheme | 单元格组风格。可选项:default/card | | title | String? | - | 单元格组标题 | | titleWidget | Widget? | - | 单元格组标题组件 | -``` -``` ### TCellStyle #### 简介 @@ -88,6 +84,47 @@ #### 工厂构造方法 -| 名称 | 说明 | -| --- | --- | -| TCellStyle.cellStyle | 生成单元格默认样式 | +##### TCellStyle.cellStyle + +生成单元格默认样式 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| context | BuildContext | - | 传递context,会生成默认样式 | + + +### TCellAlign +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| top | - | +| middle | - | +| bottom | - | + + +### TCellGroupTheme +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| defaultTheme | - | +| cardTheme | - | + + +### TCellClick +#### 类型定义 + +```dart +typedef TCellClick = void Function(TCell cell); +``` + + +### CellBuilder +#### 类型定义 + +```dart +typedef CellBuilder = Widget Function(BuildContext context, TCell cell, int index); +``` diff --git a/tdesign-component/example/assets/api/checkbox_api.md b/tdesign-component/example/assets/api/checkbox_api.md index 0d77c411c..40e8991af 100644 --- a/tdesign-component/example/assets/api/checkbox_api.md +++ b/tdesign-component/example/assets/api/checkbox_api.md @@ -7,16 +7,16 @@ | backgroundColor | Color? | - | 背景颜色 | | cardMode | bool | false | 展示为卡片模式 | | checkBoxLeftSpace | double? | - | 选项框左侧间距 | -| checked | bool | false | 选中状态。默认为`false` | +| checked | bool | false | 选中状态。默认为`false` 当FuiCheckBox嵌入到FuiCheckBoxGroup的时候,这个值表示初始状态,后续的状态会由Group管理 | | contentDirection | TContentDirection | TContentDirection.right | 文字相对icon的方位 | | customContentBuilder | ContentBuilder? | - | 完全自定义内容 | | customIconBuilder | IconBuilder? | - | 自定义Checkbox显示样式 | | customSpace | EdgeInsetsGeometry? | - | 自定义组件间距 | | disableColor | Color? | - | 禁用选择颜色 | | enable | bool | true | 不可用 | -| id | String? | - | id | +| id | String? | - | id 当FuiCheckBox嵌入到FuiCheckBoxGroup内时,这个值需要赋值,否则不会被纳入Group管理 | | insetSpacing | double? | 16 | 文字和非图标侧的距离 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | onCheckBoxChanged | OnCheckValueChanged? | - | 切换监听 | | selectColor | Color? | - | 选择颜色 | | showDivider | bool | true | 是否展示分割线 | @@ -32,8 +32,6 @@ | titleFont | Font? | - | 标题字体大小 | | titleMaxLine | int? | - | 标题的行数 | -``` -``` ### TCheckboxGroup #### 默认构造方法 @@ -41,15 +39,96 @@ | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | checkedIds | List? | - | 勾选的CheckBox id列表 | -| child | | - | | +| child | Widget | - | 可以是任意包含TCheckBox的容器,比如: ``` Row( children: [ TCheckBox(), TCheckBox(), ... ] ) ``` | | contentDirection | TContentDirection? | - | 文字相对icon的方位 | | controller | TCheckboxGroupController? | - | 可以通过控制器操作勾选状态 | | customContentBuilder | ContentBuilder? | - | CheckBox完全自定义内容 | | customIconBuilder | IconBuilder? | - | 自定义选择icon的样式 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | maxChecked | int? | - | 最多可以勾选多少 | | onChangeGroup | OnGroupChange? | - | 状态变化监听器 | | onOverloadChecked | VoidCallback? | - | 超过最大可勾选的个数 | | spacing | double? | - | CheckBoxicon和文字的距离 | | style | TCheckboxStyle? | - | CheckBox复选框样式:圆形或方形 | | titleMaxLine | int? | - | CheckBox标题的行数 | + + +### TCheckboxStyle +#### 简介 +选择框的样式 +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| circle | - | +| square | - | +| check | - | + + +### TContentDirection +#### 简介 +内容相对icon的位置,上、下、左、右,默认内容在icon的右边 +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| left | - | +| right | - | + + +### TCheckBoxSize +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| large | - | +| small | - | + + +### IconBuilder +#### 简介 +自定义Icon +#### 类型定义 + +```dart +typedef IconBuilder = Widget? Function(BuildContext context, bool checked); +``` + + +### ContentBuilder +#### 简介 +自定义Content +#### 类型定义 + +```dart +typedef ContentBuilder = Widget Function(BuildContext context, bool checked, String? content); +``` + + +### OnCheckValueChanged +#### 类型定义 + +```dart +typedef OnCheckValueChanged = void Function(bool selected); +``` + + +### OnGroupChange +#### 简介 +CheckBoxGroup变化监听器 +#### 类型定义 + +```dart +typedef OnGroupChange = void Function(List checkedIds); +``` + + +### OnCheckBoxGroupChange +#### 类型定义 + +```dart +typedef OnCheckBoxGroupChange = void Function(List ids); +``` diff --git a/tdesign-component/example/assets/api/collapse_api.md b/tdesign-component/example/assets/api/collapse_api.md index 28e0ebbd2..bc0ef877e 100644 --- a/tdesign-component/example/assets/api/collapse_api.md +++ b/tdesign-component/example/assets/api/collapse_api.md @@ -9,13 +9,39 @@ | animationDuration | Duration | kThemeAnimationDuration | 折叠面板列表的动画时长 | | children | List | - | 折叠面板列表的子组件 | | elevation | double | 0 | 折叠面板列表的阴影 | -| expansionCallback | ExpansionPanelCallback? | - | 折叠面板列表的回调函数; | -| key | | - | | -| style | TCollapseStyle | TCollapseStyle.block | 折叠面板列表的样式 | +| expansionCallback | ExpansionPanelCallback? | - | 折叠面板列表的回调函数; 回调时,入参为当前点击的折叠面板的索引 index 和是否展开的状态 isExpanded | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | +| style | TCollapseStyle | TCollapseStyle.block | 折叠面板列表的样式 - [TCollapseStyle.block] 通栏风格 - [TCollapseStyle.card] 卡片风格 | + +#### 公开属性 + +| 属性 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| initialOpenPanelValue | Object? | - | 折叠面板列表的默认展开面板的值; 当使用 [TCollapse.accordion] 时,此值生效 | #### 工厂构造方法 -| 名称 | 说明 | -| --- | --- | -| TCollapse.accordion | | +##### TCollapse.accordion + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| children | List | - | 折叠面板列表的子组件 | +| style | TCollapseStyle | TCollapseStyle.block | 折叠面板列表的样式 - [TCollapseStyle.block] 通栏风格 - [TCollapseStyle.card] 卡片风格 | +| expansionCallback | ExpansionPanelCallback? | - | 折叠面板列表的回调函数; 回调时,入参为当前点击的折叠面板的索引 index 和是否展开的状态 isExpanded | +| animationDuration | Duration | kThemeAnimationDuration | 折叠面板列表的动画时长 | +| elevation | double | 0 | 折叠面板列表的阴影 | +| initialOpenPanelValue | Object? | - | 折叠面板列表的默认展开面板的值; 当使用 [TCollapse.accordion] 时,此值生效 | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | + + +### TCollapseStyle +#### 简介 +折叠面板的组件样式 +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| block | Block 通栏风格 | +| card | Card 卡片风格 | diff --git a/tdesign-component/example/assets/api/dialog_api.md b/tdesign-component/example/assets/api/dialog_api.md index 9dd84999d..0679ea786 100644 --- a/tdesign-component/example/assets/api/dialog_api.md +++ b/tdesign-component/example/assets/api/dialog_api.md @@ -5,19 +5,19 @@ | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | backgroundColor | Color? | - | 背景颜色 | -| buttonStyle | | TDialogButtonStyle.normal | | +| buttonStyle | TDialogButtonStyle | TDialogButtonStyle.normal | - | | buttonWidget | Widget? | - | 自定义按钮 | | content | String? | - | 内容 | | contentColor | Color? | - | 内容颜色 | | contentMaxHeight | double | 0 | 内容的最大高度,默认为0,也就是不限制高度 | | contentWidget | Widget? | - | 内容Widget | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | leftBtn | TDialogButtonOptions? | - | 左侧按钮配置 | -| leftBtnAction | Function()? | - | 左侧按钮默认点击 | +| leftBtnAction | Function()? | - | 左侧按钮默认点击 | | padding | EdgeInsets? | const EdgeInsets.fromLTRB(24, 32, 24, 0) | 内容内边距 | | radius | double | 12.0 | 圆角 | | rightBtn | TDialogButtonOptions? | - | 右侧按钮配置 | -| rightBtnAction | Function()? | - | 右侧按钮默认点击 | +| rightBtnAction | Function()? | - | 右侧按钮默认点击 | | showCloseButton | bool? | - | 显示右上角关闭按钮 | | title | String? | - | 标题 | | titleAlignment | AlignmentGeometry? | - | 标题对齐模式 | @@ -26,21 +26,36 @@ #### 工厂构造方法 -| 名称 | 说明 | -| --- | --- | -| TAlertDialog.vertical | 纵向按钮排列的对话框 +##### TAlertDialog.vertical - [buttons]参数是必须的,纵向按钮默认样式都是[TButtonTheme.primary] | +纵向按钮排列的对话框 + + [buttons]参数是必须的,纵向按钮默认样式都是[TButtonTheme.primary] + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | +| buttons | List | - | - | +| backgroundColor | Color? | - | 背景颜色 | +| radius | double | 12.0 | 圆角 | +| title | String? | - | 标题 | +| titleColor | Color? | - | 标题颜色 | +| titleAlignment | AlignmentGeometry? | - | 标题对齐模式 | +| contentWidget | Widget? | - | 内容Widget | +| content | String? | - | 内容 | +| contentColor | Color? | - | 内容颜色 | +| contentMaxHeight | double | 0 | 内容的最大高度,默认为0,也就是不限制高度 | +| showCloseButton | bool? | - | 显示右上角关闭按钮 | +| padding | EdgeInsets? | const EdgeInsets.fromLTRB(24, 32, 24, 0) | 内容内边距 | +| buttonWidget | Widget? | - | 自定义按钮 | -``` -``` ### TConfirmDialog #### 默认构造方法 | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | -| action | Function()? | - | 点击 | +| action | Function()? | - | 点击 | | backgroundColor | Color? | - | 背景颜色 | | buttonStyle | TDialogButtonStyle | TDialogButtonStyle.normal | 按钮样式 | | buttonStyleCustom | TButtonStyle? | - | 按钮自定义样式属性,背景色、边框... | @@ -51,35 +66,31 @@ | contentColor | Color? | - | 内容颜色 | | contentMaxHeight | double | 0 | 内容的最大高度,默认为0,也就是不限制高度 | | contentWidget | Widget? | - | 内容Widget | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | padding | EdgeInsets? | const EdgeInsets.fromLTRB(24, 32, 24, 0) | 内容内边距 | | radius | double | 12.0 | 圆角 | | showCloseButton | bool? | - | 右上角关闭按钮 | | title | String? | - | 标题 | | titleAlignment | AlignmentGeometry? | - | 标题对齐模式 | | titleColor | Color? | - | 标题颜色 | -| width | | - | | +| width | double? | - | - | -``` -``` ### TDialogButtonOptions #### 默认构造方法 | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | -| action | Function()? | - | 点击操作 | +| action | Function()? | - | 点击操作 | | fontWeight | FontWeight? | - | 字体粗细 | -| height | double? | - | 按钮高度 | -| style | TButtonStyle? | - | 按钮样式 | +| height | double? | - | 按钮高度 建议使用默认高度 | +| style | TButtonStyle? | - | 按钮样式 设置单个按钮的样式会覆盖Dialog的默认样式 | | theme | TButtonTheme? | - | 按钮类型 | | title | String | - | 标题内容 | | titleColor | Color? | - | 标题颜色 | | titleSize | double? | - | 字体大小 | | type | TButtonType? | - | 按钮类型 | -``` -``` ### TDialogScaffold #### 默认构造方法 @@ -88,25 +99,21 @@ | --- | --- | --- | --- | | backgroundColor | Color? | - | 背景色 | | body | Widget | - | Dialog主体 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | radius | double | 12.0 | 圆角 | | showCloseButton | bool? | - | 显示右上角关闭按钮 | | width | double? | - | 弹窗宽度 | -``` -``` ### TDialogTitle #### 默认构造方法 | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | title | String? | - | 标题文字 | | titleColor | Color? | - | 标题颜色 | -``` -``` ### TDialogContent #### 默认构造方法 @@ -115,10 +122,8 @@ | --- | --- | --- | --- | | content | String? | - | 标题文字 | | contentColor | Color? | - | 标题颜色 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | -``` -``` ### TDialogInfoWidget #### 默认构造方法 @@ -129,38 +134,32 @@ | contentColor | Color? | - | 内容颜色 | | contentMaxHeight | double | 0 | 内容的最大高度,默认为0,也就是不限制高度 | | contentWidget | Widget? | - | 内容Widget | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | padding | EdgeInsetsGeometry? | const EdgeInsets.fromLTRB(24, 32, 24, 0) | 内容的内边距 | | title | String? | - | 标题 | | titleAlignment | AlignmentGeometry? | - | 标题对齐模式 | | titleColor | Color? | - | 标题颜色 | -``` -``` ### HorizontalNormalButtons #### 默认构造方法 | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | leftBtn | TDialogButtonOptions | - | 左按钮 | | rightBtn | TDialogButtonOptions | - | 右按钮 | -``` -``` ### HorizontalTextButtons #### 默认构造方法 | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | leftBtn | TDialogButtonOptions | - | 左按钮 | | rightBtn | TDialogButtonOptions | - | 右按钮 | -``` -``` ### TDialogButton #### 默认构造方法 @@ -176,12 +175,10 @@ | buttonType | TButtonType? | - | 按钮类型 | | height | double? | 40.0 | 按钮高度 | | isBlock | bool | true | 按钮高度 | -| key | | - | | -| onPressed | Function() | - | 点击 | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | +| onPressed | Function() | - | 点击 | | width | double? | - | 按钮宽度 | -``` -``` ### TImageDialog #### 默认构造方法 @@ -195,7 +192,7 @@ | contentWidget | Widget? | - | 内容Widget | | image | Image | - | 图片 | | imagePosition | TDialogImagePosition? | TDialogImagePosition.top | 图片位置 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | leftBtn | TDialogButtonOptions? | - | 左侧按钮配置 | | padding | EdgeInsets? | - | 内容内边距 | | radius | double | 12.0 | 圆角 | @@ -205,8 +202,6 @@ | titleAlignment | AlignmentGeometry? | - | 标题对齐模式 | | titleColor | Color? | - | 标题颜色 | -``` -``` ### TInputDialog #### 默认构造方法 @@ -220,7 +215,7 @@ | contentWidget | Widget? | - | 内容Widget | | customInputWidget | Widget? | - | 自定义输入框 | | hintText | String? | '' | 输入提示 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | leftBtn | TDialogButtonOptions? | - | 左侧按钮配置 | | padding | EdgeInsets? | const EdgeInsets.fromLTRB(24, 32, 24, 0) | 内容内边距 | | radius | double | 12.0 | 圆角 | @@ -230,3 +225,28 @@ | title | String? | - | 标题 | | titleAlignment | AlignmentGeometry? | - | 标题对齐模式 | | titleColor | Color? | - | 标题颜色 | + + +### TDialogButtonStyle +#### 简介 +Dialog按钮样式 + + 用于在Dialog层面配置按钮样式 + Dialog内支持配置每个按钮的样式 +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| normal | - | +| text | - | + + +### TDialogImagePosition +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| top | - | +| middle | - | diff --git a/tdesign-component/example/assets/api/divider_api.md b/tdesign-component/example/assets/api/divider_api.md index 19030afc5..72e5ed264 100644 --- a/tdesign-component/example/assets/api/divider_api.md +++ b/tdesign-component/example/assets/api/divider_api.md @@ -11,9 +11,20 @@ | height | double | 0.5 | 高度,横向线条使用 | | hideLine | bool | false | 隐藏线条,使用纯文本分割 | | isDashed | bool | false | 是否为虚线 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | margin | EdgeInsetsGeometry? | - | 外部填充 | | text | String? | - | 文本字符串,使用默认样式 | | textStyle | TextStyle? | - | 自定义文本样式 | | widget | Widget? | - | 中间控件,可自定义样式 | | width | double? | - | 宽度,需要竖向线条时使用 | + + +### TextAlignment +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| left | - | +| center | - | +| right | - | diff --git a/tdesign-component/example/assets/api/drawer_api.md b/tdesign-component/example/assets/api/drawer_api.md index 6dcef5beb..9ca2934fa 100644 --- a/tdesign-component/example/assets/api/drawer_api.md +++ b/tdesign-component/example/assets/api/drawer_api.md @@ -6,11 +6,11 @@ | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | +| context | BuildContext | - | 上下文 | | backgroundColor | Color? | - | 组件背景颜色 | | bordered | bool? | true | 是否显示边框 | | closeOnOverlayClick | bool? | true | 点击蒙层时是否关闭抽屉 | | contentWidget | Widget? | - | 自定义内容,优先级高于[items]/[footer]/[title] | -| context | BuildContext | context | 上下文 | | drawerTop | double? | - | 距离顶部的距离 | | footer | Widget? | - | 抽屉的底部 | | hover | bool? | true | 是否开启点击反馈 | @@ -26,8 +26,6 @@ | visible | bool? | - | 组件是否可见 | | width | double? | 280 | 宽度 | -``` -``` ### TDrawerWidget #### 简介 @@ -44,15 +42,13 @@ | hover | bool? | true | 是否开启点击反馈 | | isShowLastBordered | bool? | true | 是否显示最后一行分割线 | | items | List? | - | 抽屉里的列表项 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | onItemClick | TDrawerItemClickCallback? | - | 点击抽屉里的列表项触发 | | style | TCellStyle? | - | 列表自定义样式 | | title | String? | - | 抽屉的标题 | | titleWidget | Widget? | - | 抽屉的标题组件 | | width | double? | 280 | 宽度 | -``` -``` ### TDrawerItem #### 简介 @@ -64,3 +60,23 @@ | content | Widget? | - | 完全自定义 | | icon | Widget? | - | 每列图标 | | title | String? | - | 每列标题 | + + +### TDrawerPlacement +#### 简介 +抽屉方向 +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| left | - | +| right | - | + + +### TDrawerItemClickCallback +#### 类型定义 + +```dart +typedef TDrawerItemClickCallback = void Function(int index, TDrawerItem item); +``` diff --git a/tdesign-component/example/assets/api/dropdown-menu_api.md b/tdesign-component/example/assets/api/dropdown-menu_api.md index e19bc36b7..940cadce5 100644 --- a/tdesign-component/example/assets/api/dropdown-menu_api.md +++ b/tdesign-component/example/assets/api/dropdown-menu_api.md @@ -16,7 +16,7 @@ | height | double? | 48 | menu的高度 | | isScrollable | bool? | false | 是否开启滚动列表 | | items | List? | - | 下拉菜单 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | labelBuilder | LabelBuilder? | - | 自定义标签内容 | | onMenuClosed | ValueChanged? | - | 关闭菜单事件 | | onMenuOpened | ValueChanged? | - | 展开菜单事件 | @@ -24,8 +24,6 @@ | tabBarAlign | MainAxisAlignment? | MainAxisAlignment.center | [TDropdownItem.label]和[arrowIcon]/[TDropdownItem.arrowIcon]的对齐方式 | | width | double? | - | menu的宽度 | -``` -``` ### TDropdownItem #### 简介 @@ -39,7 +37,7 @@ | builder | TDropdownItemContentBuilder? | - | 完全自定义展示内容 | | controller | TDropdownItemController? | - | 下拉菜单控制器 | | disabled | bool? | false | 是否禁用 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | label | String? | - | 标题 | | maxHeight | double? | - | 内容最大高度 | | minHeight | double? | - | 内容最小高度 | @@ -53,8 +51,12 @@ | tabBarFlex | int? | 1 | 该item在menu上的宽度占比,仅在[TDropdownMenu.isScrollable]为false时有效 | | tabBarWidth | double? | - | 该item在menu上的宽度,仅在[TDropdownMenu.isScrollable]为true时有效 | -``` -``` +#### 静态成员 + +| 名称 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| operateHeight | double | - | - | + ### TDropdownItemOption #### 简介 @@ -71,9 +73,55 @@ | selectedColor | Color? | - | 选中颜色 | | value | String | - | 选项值 | + +### TDropdownItemController +#### 简介 +下拉菜单控制器 + +### TDropdownMenuDirection +#### 简介 +菜单展开方向 +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| down | 向下 | +| up | 向上 | +| auto | 根据内容高度动态展示方向 | + + +### TDropdownItemContentBuilder +#### 类型定义 + +```dart +typedef TDropdownItemContentBuilder = Widget Function(BuildContext context, _TDropdownItemState itemState, TDropdownPopup? popupState); ``` + + +### TDropdownItemOptionsCallback +#### 类型定义 + +```dart +typedef TDropdownItemOptionsCallback = void Function(List? options); ``` -### TDropdownItemController + +### TDropdownItemBuilder #### 简介 -下拉菜单控制器 \ No newline at end of file +下拉菜单构建器 +#### 类型定义 + +```dart +typedef TDropdownItemBuilder = List Function(BuildContext context); +``` + + +### LabelBuilder +#### 简介 +自定义标签内容 +#### 类型定义 + +```dart +typedef LabelBuilder = Widget Function(BuildContext context, String label, bool isOpened, int index); +``` diff --git a/tdesign-component/example/assets/api/empty_api.md b/tdesign-component/example/assets/api/empty_api.md index e39301b33..ba743b257 100644 --- a/tdesign-component/example/assets/api/empty_api.md +++ b/tdesign-component/example/assets/api/empty_api.md @@ -10,8 +10,26 @@ | emptyTextFont | Font? | - | 描述文字大小 | | icon | IconData? | TIcons.info_circle_filled | 图标 | | image | Widget? | - | 展示图片 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | onTapEvent | TTapEvent? | - | 点击事件 | | operationText | String? | - | 操作按钮文案 | | operationTheme | TButtonTheme? | - | 操作按钮文案主题色 | | type | TEmptyType | TEmptyType.plain | 类型,为operation有操作按钮,plain无按钮 | + + +### TEmptyType +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| plain | - | +| operation | - | + + +### TTapEvent +#### 类型定义 + +```dart +typedef TTapEvent = void Function(); +``` diff --git a/tdesign-component/example/assets/api/fab_api.md b/tdesign-component/example/assets/api/fab_api.md index 641de8127..405f3c9fd 100644 --- a/tdesign-component/example/assets/api/fab_api.md +++ b/tdesign-component/example/assets/api/fab_api.md @@ -5,9 +5,43 @@ | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | icon | Icon? | - | 图标 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | onClick | VoidCallback? | - | 点击事件 | | shape | TFabShape | TFabShape.circle | 形状 | | size | TFabSize | TFabSize.large | 大小 | | text | String? | - | 文本 | | theme | TFabTheme | TFabTheme.defaultTheme | 主题 | + + +### TFabTheme +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| primary | - | +| defaultTheme | - | +| light | - | +| danger | - | + + +### TFabShape +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| circle | - | +| square | - | + + +### TFabSize +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| large | - | +| medium | - | +| small | - | +| extraSmall | - | diff --git a/tdesign-component/example/assets/api/footer_api.md b/tdesign-component/example/assets/api/footer_api.md index 17deb0158..f5df55732 100644 --- a/tdesign-component/example/assets/api/footer_api.md +++ b/tdesign-component/example/assets/api/footer_api.md @@ -4,10 +4,21 @@ | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | +| type | TFooterType | - | 样式 | | height | double? | - | 自定义图片高 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | links | List | const [] | 链接 | | logo | String? | - | 品牌图片 | | text | String | '' | 文字 | -| type | TFooterType | type | 样式 | | width | double? | - | 自定义图片宽 | + + +### TFooterType +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| text | 文字样式 | +| link | 链接样式 | +| brand | 品牌样式 | diff --git a/tdesign-component/example/assets/api/form_api.md b/tdesign-component/example/assets/api/form_api.md index 61922773d..948830f5e 100644 --- a/tdesign-component/example/assets/api/form_api.md +++ b/tdesign-component/example/assets/api/form_api.md @@ -9,24 +9,22 @@ | data | Map | - | 表单数据 | | disabled | bool | false | 是否禁用整个表单 | | errorMessage | Object? | - | 表单信息错误信息配置 | -| formContentAlign | TextAlign | TextAlign.left | 表单内容对齐方式: 左对齐、右对齐、居中对齐 | +| formContentAlign | TextAlign | TextAlign.left | 表单内容对齐方式: 左对齐、右对齐、居中对齐 可选项: left/right/center 默认为左对齐 优先级低于 TFormItem 的对齐 API TODO: TStepper TRate 等组件没用实现通用性 | | formController | FormController? | - | 表单控制器 | -| formLabelAlign | TextAlign? | TextAlign.left | 表单字段标签的对齐方式: | -| formShowErrorMessage | bool? | true | 校验不通过时,是否显示错误提示信息,统一控制全部表单项 | +| formLabelAlign | TextAlign? | TextAlign.left | 表单字段标签的对齐方式: 左对齐、右对齐、顶部对齐 可选项: left/right/top TODO: 表单总体标签对齐方式 | +| formShowErrorMessage | bool? | true | 校验不通过时,是否显示错误提示信息,统一控制全部表单项 如果希望控制单个表单项,请给 FormItem 设置该属性 | | isHorizontal | bool | true | 表单排列方式是否为 水平方向 | | items | List | - | 表单内容 items | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | labelWidth | double? | 20.0 | 可以整体设置 label 标签宽度 | | onReset | Function? | - | 表单重置时触发 | | onSubmit | Function | - | 表单提交时触发 | -| preventSubmitDefault | bool? | true | 是否阻止表单提交默认事件(表单提交默认事件会刷新页面) | +| preventSubmitDefault | bool? | true | 是否阻止表单提交默认事件(表单提交默认事件会刷新页面) 设置为 true 可以避免刷新 | | requiredMark | bool? | true | 是否显示必填符号(*),默认显示 | | rules | Map | - | 整个表单字段校验规则 | -| scrollToFirstError | String? | - | 表单校验不通过时,是否自动滚动到第一个校验不通过的字段,平滑滚动或是瞬间直达。 | +| scrollToFirstError | String? | - | 表单校验不通过时,是否自动滚动到第一个校验不通过的字段,平滑滚动或是瞬间直达。 值为空则表示不滚动。可选项:''/smooth/auto | | submitWithWarningMessage | bool? | false | 【讨论中】当校验结果只有告警信息时,是否触发 submit 提交事件 | -``` -``` ### TFormItem #### 默认构造方法 @@ -36,20 +34,20 @@ | additionInfo | String? | - | TInput的辅助信息 | | backgroundColor | Color? | - | 背景色 | | child | Widget? | - | 表单子组件 | -| contentAlign | TextAlign? | - | 表单显示内容对齐方式: | -| formItemNotifier | | - | | +| contentAlign | TextAlign? | - | 表单显示内容对齐方式: left、right、top TODO: TStepper TRate 等组件没用实现通用性 | +| formItemNotifier | FormItemNotifier? | - | - | | formRules | List? | - | 整个表单的校验规则 | | help | String? | - | TInput 默认显示文字 | -| hintText | null | '' | 提示内容 | +| hintText | - | '' | 提示内容 | | indicator | bool? | - | TTextarea 的属性,指示器 | | itemRule | List? | - | 表单项验证规则 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | label | String? | - | 表单项标签左侧展示的内容 | -| labelAlign | TextAlign? | - | TODO: item 标签对齐方式 | +| labelAlign | TextAlign? | - | TODO: item 标签对齐方式 可选: left、right、top | | labelWidget | Widget? | - | 自定义标签 | | labelWidth | double? | - | 标签宽度,如果提供则覆盖Form的labelWidth | | name | String? | - | 表单字段名称 | -| radios | | - | | +| radios | Map? | - | - | | requiredMark | bool? | true | 是否显示必填标记(*) | | select | String | '' | 选择器 适用于日期选择器等 | | selectFn | Function? | - | 选择器方法 适用于日期选择器等 | @@ -57,8 +55,6 @@ | tipAlign | TextAlign? | - | 组件提示内容对齐方式 | | type | TFormItemType | - | 表格单元需要使用的组件类型 | -``` -``` ### TFormValidation #### 默认构造方法 @@ -68,3 +64,21 @@ | errorMessage | String | - | 错误提示信息 | | type | TFormItemType | - | 校验对象的类型 | | validate | String? Function(dynamic) | - | 校验方法 | + + +### TFormItemType +#### 简介 +表格单元选用组件类型的枚举 +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| input | - | +| radios | - | +| dateTimePicker | - | +| cascader | - | +| stepper | - | +| rate | - | +| textarea | - | +| upLoadImg | - | diff --git a/tdesign-component/example/assets/api/icon_api.md b/tdesign-component/example/assets/api/icon_api.md index 74dbb5f5b..73c5f852d 100644 --- a/tdesign-component/example/assets/api/icon_api.md +++ b/tdesign-component/example/assets/api/icon_api.md @@ -1,8 +1,2128 @@ ## API ### TIcons +#### 静态成员 + +| 名称 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| accessibility | - | - | - | +| accessibility_filled | - | - | - | +| activity | - | - | - | +| activity_filled | - | - | - | +| add | - | - | - | +| add_and_subtract | - | - | - | +| add_circle | - | - | - | +| add_circle_filled | - | - | - | +| add_rectangle | - | - | - | +| add_rectangle_filled | - | - | - | +| address_book | - | - | - | +| address_book_filled | - | - | - | +| adjustment | - | - | - | +| adjustment_filled | - | - | - | +| airplay_wave | - | - | - | +| airplay_wave_filled | - | - | - | +| alarm | - | - | - | +| alarm_add | - | - | - | +| alarm_add_filled | - | - | - | +| alarm_filled | - | - | - | +| alarm_off | - | - | - | +| alarm_off_filled | - | - | - | +| align_top | - | - | - | +| align_vertical | - | - | - | +| all | - | - | - | +| alpha | - | - | - | +| analytics | - | - | - | +| analytics_filled | - | - | - | +| anchor | - | - | - | +| angry | - | - | - | +| angry_filled | - | - | - | +| animation | - | - | - | +| animation_1 | - | - | - | +| animation_1_filled | - | - | - | +| animation_filled | - | - | - | +| anticlockwise | - | - | - | +| anticlockwise_filled | - | - | - | +| api | - | - | - | +| app | - | - | - | +| app_filled | - | - | - | +| apple | - | - | - | +| apple_filled | - | - | - | +| application | - | - | - | +| application_filled | - | - | - | +| architecture_hui_style | - | - | - | +| architecture_hui_style_filled | - | - | - | +| archway | - | - | - | +| archway_1 | - | - | - | +| archway_1_filled | - | - | - | +| archway_filled | - | - | - | +| arrow_down | - | - | - | +| arrow_down_circle | - | - | - | +| arrow_down_circle_filled | - | - | - | +| arrow_down_rectangle | - | - | - | +| arrow_down_rectangle_filled | - | - | - | +| arrow_left | - | - | - | +| arrow_left_circle | - | - | - | +| arrow_left_circle_filled | - | - | - | +| arrow_left_down | - | - | - | +| arrow_left_down_circle | - | - | - | +| arrow_left_down_circle_filled | - | - | - | +| arrow_left_right_1 | - | - | - | +| arrow_left_right_2 | - | - | - | +| arrow_left_right_3 | - | - | - | +| arrow_left_right_circle | - | - | - | +| arrow_left_right_circle_filled | - | - | - | +| arrow_left_up | - | - | - | +| arrow_left_up_circle | - | - | - | +| arrow_left_up_circle_filled | - | - | - | +| arrow_right | - | - | - | +| arrow_right_circle | - | - | - | +| arrow_right_circle_filled | - | - | - | +| arrow_right_down | - | - | - | +| arrow_right_down_circle | - | - | - | +| arrow_right_down_circle_filled | - | - | - | +| arrow_right_up | - | - | - | +| arrow_right_up_circle | - | - | - | +| arrow_right_up_circle_filled | - | - | - | +| arrow_triangle_down | - | - | - | +| arrow_triangle_down_filled | - | - | - | +| arrow_triangle_up | - | - | - | +| arrow_triangle_up_filled | - | - | - | +| arrow_up | - | - | - | +| arrow_up_circle | - | - | - | +| arrow_up_circle_filled | - | - | - | +| arrow_up_down_1 | - | - | - | +| arrow_up_down_2 | - | - | - | +| arrow_up_down_3 | - | - | - | +| arrow_up_down_circle | - | - | - | +| arrow_up_down_circle_filled | - | - | - | +| artboard | - | - | - | +| article | - | - | - | +| article_filled | - | - | - | +| assignment | - | - | - | +| assignment_checked | - | - | - | +| assignment_checked_filled | - | - | - | +| assignment_code | - | - | - | +| assignment_code_filled | - | - | - | +| assignment_error | - | - | - | +| assignment_error_filled | - | - | - | +| assignment_filled | - | - | - | +| assignment_user | - | - | - | +| assignment_user_filled | - | - | - | +| attach | - | - | - | +| attic | - | - | - | +| attic_1 | - | - | - | +| attic_1_filled | - | - | - | +| attic_filled | - | - | - | +| audio | - | - | - | +| audio_filled | - | - | - | +| awkward | - | - | - | +| awkward_filled | - | - | - | +| backtop | - | - | - | +| backtop_rectangle | - | - | - | +| backtop_rectangle_filled | - | - | - | +| backup | - | - | - | +| backup_filled | - | - | - | +| backward | - | - | - | +| backward_filled | - | - | - | +| bad_laugh | - | - | - | +| bad_laugh_filled | - | - | - | +| bamboo_shoot | - | - | - | +| bamboo_shoot_filled | - | - | - | +| banana | - | - | - | +| banana_filled | - | - | - | +| barbecue | - | - | - | +| barbecue_filled | - | - | - | +| barcode | - | - | - | +| barcode_1 | - | - | - | +| base_station | - | - | - | +| battery | - | - | - | +| battery_add | - | - | - | +| battery_add_filled | - | - | - | +| battery_charging | - | - | - | +| battery_charging_filled | - | - | - | +| battery_filled | - | - | - | +| battery_low | - | - | - | +| battery_low_filled | - | - | - | +| bean | - | - | - | +| bean_filled | - | - | - | +| beer | - | - | - | +| beer_filled | - | - | - | +| beta | - | - | - | +| bifurcate | - | - | - | +| bifurcate_filled | - | - | - | +| bill | - | - | - | +| bill_filled | - | - | - | +| bluetooth | - | - | - | +| bone | - | - | - | +| bone_filled | - | - | - | +| book | - | - | - | +| book_filled | - | - | - | +| book_open | - | - | - | +| book_open_filled | - | - | - | +| book_unknown | - | - | - | +| book_unknown_filled | - | - | - | +| bookmark | - | - | - | +| bookmark_add | - | - | - | +| bookmark_add_filled | - | - | - | +| bookmark_checked | - | - | - | +| bookmark_checked_filled | - | - | - | +| bookmark_double | - | - | - | +| bookmark_double_filled | - | - | - | +| bookmark_filled | - | - | - | +| bookmark_minus | - | - | - | +| bookmark_minus_filled | - | - | - | +| braces | - | - | - | +| brackets | - | - | - | +| bread | - | - | - | +| bread_filled | - | - | - | +| bridge | - | - | - | +| bridge_1 | - | - | - | +| bridge_1_filled | - | - | - | +| bridge_2 | - | - | - | +| bridge_2_filled | - | - | - | +| bridge_3 | - | - | - | +| bridge_4 | - | - | - | +| bridge_5 | - | - | - | +| bridge_5_filled | - | - | - | +| bridge_6 | - | - | - | +| bridge_6_filled | - | - | - | +| brightness | - | - | - | +| brightness_1 | - | - | - | +| brightness_1_filled | - | - | - | +| brightness_filled | - | - | - | +| broccoli | - | - | - | +| broccoli_filled | - | - | - | +| browse | - | - | - | +| browse_filled | - | - | - | +| browse_gallery | - | - | - | +| browse_gallery_filled | - | - | - | +| browse_off | - | - | - | +| browse_off_filled | - | - | - | +| brush | - | - | - | +| brush_filled | - | - | - | +| bug | - | - | - | +| bug_filled | - | - | - | +| bug_report | - | - | - | +| bug_report_filled | - | - | - | +| building | - | - | - | +| building_1 | - | - | - | +| building_1_filled | - | - | - | +| building_2 | - | - | - | +| building_2_filled | - | - | - | +| building_3 | - | - | - | +| building_3_filled | - | - | - | +| building_4 | - | - | - | +| building_4_filled | - | - | - | +| building_5 | - | - | - | +| building_5_filled | - | - | - | +| building_filled | - | - | - | +| bulletpoint | - | - | - | +| button | - | - | - | +| button_filled | - | - | - | +| cabbage | - | - | - | +| cabbage_filled | - | - | - | +| cake | - | - | - | +| cake_filled | - | - | - | +| calculation | - | - | - | +| calculation_1 | - | - | - | +| calculation_1_filled | - | - | - | +| calculator | - | - | - | +| calculator_1 | - | - | - | +| calculator_filled | - | - | - | +| calendar | - | - | - | +| calendar_1 | - | - | - | +| calendar_1_filled | - | - | - | +| calendar_2 | - | - | - | +| calendar_2_filled | - | - | - | +| calendar_edit | - | - | - | +| calendar_edit_filled | - | - | - | +| calendar_event | - | - | - | +| calendar_event_filled | - | - | - | +| calendar_filled | - | - | - | +| call | - | - | - | +| call_1 | - | - | - | +| call_1_filled | - | - | - | +| call_cancel | - | - | - | +| call_cancel_filled | - | - | - | +| call_filled | - | - | - | +| call_forwarded | - | - | - | +| call_forwarded_filled | - | - | - | +| call_incoming | - | - | - | +| call_incoming_filled | - | - | - | +| call_off | - | - | - | +| call_off_filled | - | - | - | +| calm | - | - | - | +| calm_1 | - | - | - | +| calm_1_filled | - | - | - | +| calm_filled | - | - | - | +| camera | - | - | - | +| camera_1 | - | - | - | +| camera_1_filled | - | - | - | +| camera_2 | - | - | - | +| camera_2_filled | - | - | - | +| camera_filled | - | - | - | +| camera_off | - | - | - | +| camera_off_filled | - | - | - | +| candy | - | - | - | +| candy_filled | - | - | - | +| card | - | - | - | +| card_filled | - | - | - | +| cardmembership | - | - | - | +| cardmembership_filled | - | - | - | +| caret_down | - | - | - | +| caret_down_small | - | - | - | +| caret_left | - | - | - | +| caret_left_small | - | - | - | +| caret_right | - | - | - | +| caret_right_small | - | - | - | +| caret_up | - | - | - | +| caret_up_small | - | - | - | +| cart | - | - | - | +| cart_add | - | - | - | +| cart_add_filled | - | - | - | +| cart_filled | - | - | - | +| cast | - | - | - | +| cast_filled | - | - | - | +| castle | - | - | - | +| castle_1 | - | - | - | +| castle_1_filled | - | - | - | +| castle_2 | - | - | - | +| castle_2_filled | - | - | - | +| castle_3 | - | - | - | +| castle_3_filled | - | - | - | +| castle_4 | - | - | - | +| castle_4_filled | - | - | - | +| castle_5 | - | - | - | +| castle_5_filled | - | - | - | +| castle_6 | - | - | - | +| castle_6_filled | - | - | - | +| castle_7 | - | - | - | +| castle_7_filled | - | - | - | +| castle_filled | - | - | - | +| cat | - | - | - | +| cat_filled | - | - | - | +| catalog | - | - | - | +| catalog_filled | - | - | - | +| cd | - | - | - | +| cd_filled | - | - | - | +| celsius | - | - | - | +| center_focus_strong | - | - | - | +| center_focus_strong_filled | - | - | - | +| centimeter | - | - | - | +| certificate | - | - | - | +| certificate_1 | - | - | - | +| certificate_1_filled | - | - | - | +| certificate_filled | - | - | - | +| chart | - | - | - | +| chart_3d | - | - | - | +| chart_3d_filled | - | - | - | +| chart_add | - | - | - | +| chart_add_filled | - | - | - | +| chart_analytics | - | - | - | +| chart_area | - | - | - | +| chart_area_filled | - | - | - | +| chart_area_multi | - | - | - | +| chart_area_multi_filled | - | - | - | +| chart_bar | - | - | - | +| chart_bar_filled | - | - | - | +| chart_bubble | - | - | - | +| chart_bubble_filled | - | - | - | +| chart_column | - | - | - | +| chart_column_filled | - | - | - | +| chart_combo | - | - | - | +| chart_combo_filled | - | - | - | +| chart_filled | - | - | - | +| chart_line | - | - | - | +| chart_line_data | - | - | - | +| chart_line_data_1 | - | - | - | +| chart_line_multi | - | - | - | +| chart_maximum | - | - | - | +| chart_median | - | - | - | +| chart_minimum | - | - | - | +| chart_pie | - | - | - | +| chart_pie_filled | - | - | - | +| chart_radar | - | - | - | +| chart_radar_filled | - | - | - | +| chart_radial | - | - | - | +| chart_ring | - | - | - | +| chart_ring_1 | - | - | - | +| chart_ring_1_filled | - | - | - | +| chart_ring_filled | - | - | - | +| chart_scatter | - | - | - | +| chart_stacked | - | - | - | +| chart_stacked_filled | - | - | - | +| chat | - | - | - | +| chat_add | - | - | - | +| chat_add_filled | - | - | - | +| chat_bubble | - | - | - | +| chat_bubble_1 | - | - | - | +| chat_bubble_1_filled | - | - | - | +| chat_bubble_add | - | - | - | +| chat_bubble_add_filled | - | - | - | +| chat_bubble_error | - | - | - | +| chat_bubble_error_filled | - | - | - | +| chat_bubble_filled | - | - | - | +| chat_bubble_help | - | - | - | +| chat_bubble_help_filled | - | - | - | +| chat_bubble_history | - | - | - | +| chat_bubble_history_filled | - | - | - | +| chat_bubble_locked | - | - | - | +| chat_bubble_locked_filled | - | - | - | +| chat_bubble_smile | - | - | - | +| chat_bubble_smile_filled | - | - | - | +| chat_checked | - | - | - | +| chat_checked_filled | - | - | - | +| chat_clear | - | - | - | +| chat_clear_filled | - | - | - | +| chat_double | - | - | - | +| chat_double_filled | - | - | - | +| chat_error | - | - | - | +| chat_error_filled | - | - | - | +| chat_filled | - | - | - | +| chat_heart | - | - | - | +| chat_heart_filled | - | - | - | +| chat_message | - | - | - | +| chat_message_filled | - | - | - | +| chat_off | - | - | - | +| chat_off_filled | - | - | - | +| chat_poll | - | - | - | +| chat_poll_filled | - | - | - | +| chat_setting | - | - | - | +| chat_setting_filled | - | - | - | +| check | - | - | - | +| check_circle | - | - | - | +| check_circle_filled | - | - | - | +| check_double | - | - | - | +| check_rectangle | - | - | - | +| check_rectangle_filled | - | - | - | +| cheese | - | - | - | +| cheese_filled | - | - | - | +| cherry | - | - | - | +| cherry_filled | - | - | - | +| chevron_down | - | - | - | +| chevron_down_circle | - | - | - | +| chevron_down_circle_filled | - | - | - | +| chevron_down_double | - | - | - | +| chevron_down_double_s | - | - | - | +| chevron_down_rectangle | - | - | - | +| chevron_down_rectangle_filled | - | - | - | +| chevron_down_s | - | - | - | +| chevron_left | - | - | - | +| chevron_left_circle | - | - | - | +| chevron_left_circle_filled | - | - | - | +| chevron_left_double | - | - | - | +| chevron_left_double_s | - | - | - | +| chevron_left_rectangle | - | - | - | +| chevron_left_rectangle_filled | - | - | - | +| chevron_left_s | - | - | - | +| chevron_right | - | - | - | +| chevron_right_circle | - | - | - | +| chevron_right_circle_filled | - | - | - | +| chevron_right_double | - | - | - | +| chevron_right_double_s | - | - | - | +| chevron_right_rectangle | - | - | - | +| chevron_right_rectangle_filled | - | - | - | +| chevron_right_s | - | - | - | +| chevron_up | - | - | - | +| chevron_up_circle | - | - | - | +| chevron_up_circle_filled | - | - | - | +| chevron_up_double | - | - | - | +| chevron_up_double_s | - | - | - | +| chevron_up_rectangle | - | - | - | +| chevron_up_rectangle_filled | - | - | - | +| chevron_up_s | - | - | - | +| chicken | - | - | - | +| chili | - | - | - | +| chili_filled | - | - | - | +| chimney | - | - | - | +| chimney_1 | - | - | - | +| chimney_1_filled | - | - | - | +| chimney_2 | - | - | - | +| chimney_2_filled | - | - | - | +| chimney_filled | - | - | - | +| chinese_cabbage | - | - | - | +| chinese_cabbage_filled | - | - | - | +| church | - | - | - | +| church_filled | - | - | - | +| circle | - | - | - | +| circle_filled | - | - | - | +| city | - | - | - | +| city_1 | - | - | - | +| city_10 | - | - | - | +| city_10_filled | - | - | - | +| city_11 | - | - | - | +| city_11_filled | - | - | - | +| city_12 | - | - | - | +| city_12_filled | - | - | - | +| city_13 | - | - | - | +| city_13_filled | - | - | - | +| city_14 | - | - | - | +| city_14_filled | - | - | - | +| city_15 | - | - | - | +| city_15_filled | - | - | - | +| city_1_filled | - | - | - | +| city_2 | - | - | - | +| city_2_filled | - | - | - | +| city_3 | - | - | - | +| city_3_filled | - | - | - | +| city_4 | - | - | - | +| city_4_filled | - | - | - | +| city_5 | - | - | - | +| city_5_filled | - | - | - | +| city_6 | - | - | - | +| city_6_filled | - | - | - | +| city_7 | - | - | - | +| city_7_filled | - | - | - | +| city_8 | - | - | - | +| city_8_filled | - | - | - | +| city_9 | - | - | - | +| city_9_filled | - | - | - | +| city_ancient | - | - | - | +| city_ancient_1 | - | - | - | +| city_ancient_1_filled | - | - | - | +| city_ancient_2 | - | - | - | +| city_ancient_2_filled | - | - | - | +| city_ancient_filled | - | - | - | +| city_filled | - | - | - | +| clear | - | - | - | +| clear_filled | - | - | - | +| clear_formatting | - | - | - | +| clear_formatting_1 | - | - | - | +| clear_formatting_1_filled | - | - | - | +| clear_formatting_filled | - | - | - | +| close | - | - | - | +| close_circle | - | - | - | +| close_circle_filled | - | - | - | +| close_octagon | - | - | - | +| close_octagon_filled | - | - | - | +| close_rectangle | - | - | - | +| close_rectangle_filled | - | - | - | +| cloud | - | - | - | +| cloud_download | - | - | - | +| cloud_filled | - | - | - | +| cloud_upload | - | - | - | +| cloudy_day | - | - | - | +| cloudy_day_filled | - | - | - | +| cloudy_night | - | - | - | +| cloudy_night_filled | - | - | - | +| cloudy_night_rain | - | - | - | +| cloudy_night_rain_filled | - | - | - | +| cloudy_rain | - | - | - | +| cloudy_rain_filled | - | - | - | +| cloudy_sunny | - | - | - | +| cloudy_sunny_filled | - | - | - | +| code | - | - | - | +| code_1 | - | - | - | +| code_off | - | - | - | +| cola | - | - | - | +| cola_filled | - | - | - | +| collage | - | - | - | +| collage_filled | - | - | - | +| collection | - | - | - | +| collection_filled | - | - | - | +| color_invert | - | - | - | +| color_invert_filled | - | - | - | +| combination | - | - | - | +| combination_filled | - | - | - | +| command | - | - | - | +| compass | - | - | - | +| compass_1 | - | - | - | +| compass_1_filled | - | - | - | +| compass_filled | - | - | - | +| component_breadcrumb | - | - | - | +| component_breadcrumb_filled | - | - | - | +| component_checkbox | - | - | - | +| component_checkbox_filled | - | - | - | +| component_divider_horizontal | - | - | - | +| component_divider_horizontal_filled | - | - | - | +| component_divider_vertical | - | - | - | +| component_divider_vertical_filled | - | - | - | +| component_dropdown | - | - | - | +| component_dropdown_filled | - | - | - | +| component_grid | - | - | - | +| component_grid_filled | - | - | - | +| component_input | - | - | - | +| component_input_filled | - | - | - | +| component_layout | - | - | - | +| component_layout_filled | - | - | - | +| component_radio | - | - | - | +| component_space | - | - | - | +| component_space_filled | - | - | - | +| component_steps | - | - | - | +| component_steps_filled | - | - | - | +| component_switch | - | - | - | +| component_switch_filled | - | - | - | +| constraint | - | - | - | +| contrast | - | - | - | +| contrast_1 | - | - | - | +| contrast_1_filled | - | - | - | +| contrast_filled | - | - | - | +| control_platform | - | - | - | +| control_platform_filled | - | - | - | +| cooperate | - | - | - | +| cooperate_filled | - | - | - | +| coordinate_system | - | - | - | +| coordinate_system_filled | - | - | - | +| copy | - | - | - | +| copy_filled | - | - | - | +| copyright | - | - | - | +| copyright_filled | - | - | - | +| corn | - | - | - | +| corn_filled | - | - | - | +| coupon | - | - | - | +| coupon_filled | - | - | - | +| course | - | - | - | +| course_filled | - | - | - | +| cpu | - | - | - | +| cpu_filled | - | - | - | +| crack | - | - | - | +| crack_filled | - | - | - | +| creditcard | - | - | - | +| creditcard_add | - | - | - | +| creditcard_add_filled | - | - | - | +| creditcard_filled | - | - | - | +| creditcard_off | - | - | - | +| creditcard_off_filled | - | - | - | +| crooked_smile | - | - | - | +| crooked_smile_filled | - | - | - | +| cry_and_laugh | - | - | - | +| cry_and_laugh_filled | - | - | - | +| cry_loudly | - | - | - | +| cry_loudly_filled | - | - | - | +| css3 | - | - | - | +| css3_filled | - | - | - | +| cucumber | - | - | - | +| currency_exchange | - | - | - | +| cursor | - | - | - | +| cursor_filled | - | - | - | +| curtain | - | - | - | +| curtain_filled | - | - | - | +| curve | - | - | - | +| cut | - | - | - | +| cut_1 | - | - | - | +| dam | - | - | - | +| dam_1 | - | - | - | +| dam_1_filled | - | - | - | +| dam_2 | - | - | - | +| dam_2_filled | - | - | - | +| dam_3 | - | - | - | +| dam_3_filled | - | - | - | +| dam_4 | - | - | - | +| dam_4_filled | - | - | - | +| dam_5 | - | - | - | +| dam_5_filled | - | - | - | +| dam_6 | - | - | - | +| dam_6_filled | - | - | - | +| dam_7 | - | - | - | +| dam_7_filled | - | - | - | +| dam_filled | - | - | - | +| dart_board | - | - | - | +| dart_board_filled | - | - | - | +| dashboard | - | - | - | +| dashboard_1 | - | - | - | +| dashboard_1_filled | - | - | - | +| dashboard_filled | - | - | - | +| data | - | - | - | +| data_base | - | - | - | +| data_base_filled | - | - | - | +| data_checked | - | - | - | +| data_checked_filled | - | - | - | +| data_display | - | - | - | +| data_error | - | - | - | +| data_error_filled | - | - | - | +| data_filled | - | - | - | +| data_search | - | - | - | +| data_search_filled | - | - | - | +| delete | - | - | - | +| delete_1 | - | - | - | +| delete_1_filled | - | - | - | +| delete_filled | - | - | - | +| delete_time | - | - | - | +| delete_time_filled | - | - | - | +| delta | - | - | - | +| delta_filled | - | - | - | +| depressed | - | - | - | +| depressed_filled | - | - | - | +| desktop | - | - | - | +| desktop_1 | - | - | - | +| desktop_1_filled | - | - | - | +| desktop_filled | - | - | - | +| despise | - | - | - | +| despise_filled | - | - | - | +| device | - | - | - | +| device_filled | - | - | - | +| discount | - | - | - | +| discount_filled | - | - | - | +| dissatisfaction | - | - | - | +| dissatisfaction_filled | - | - | - | +| divide | - | - | - | +| dividers | - | - | - | +| dividers_1 | - | - | - | +| doge | - | - | - | +| doge_filled | - | - | - | +| double_storey | - | - | - | +| double_storey_filled | - | - | - | +| download | - | - | - | +| download_1 | - | - | - | +| download_2 | - | - | - | +| download_2_filled | - | - | - | +| downscale | - | - | - | +| drag_drop | - | - | - | +| drag_move | - | - | - | +| drink | - | - | - | +| drink_filled | - | - | - | +| drumstick | - | - | - | +| drumstick_filled | - | - | - | +| dv | - | - | - | +| dv_filled | - | - | - | +| dvd | - | - | - | +| dvd_filled | - | - | - | +| earphone | - | - | - | +| earphone_filled | - | - | - | +| earth | - | - | - | +| earth_filled | - | - | - | +| edit | - | - | - | +| edit_1 | - | - | - | +| edit_1_filled | - | - | - | +| edit_2 | - | - | - | +| edit_2_filled | - | - | - | +| edit_filled | - | - | - | +| edit_off | - | - | - | +| edit_off_filled | - | - | - | +| education | - | - | - | +| education_filled | - | - | - | +| eggplant | - | - | - | +| eggplant_filled | - | - | - | +| ellipsis | - | - | - | +| emo_emotional | - | - | - | +| emo_emotional_filled | - | - | - | +| enter | - | - | - | +| equal | - | - | - | +| error | - | - | - | +| error_circle | - | - | - | +| error_circle_filled | - | - | - | +| error_triangle | - | - | - | +| error_triangle_filled | - | - | - | +| excited | - | - | - | +| excited_1 | - | - | - | +| excited_1_filled | - | - | - | +| excited_filled | - | - | - | +| expand_down | - | - | - | +| expand_down_filled | - | - | - | +| expand_horizontal | - | - | - | +| expand_up | - | - | - | +| expand_up_filled | - | - | - | +| expand_vertical | - | - | - | +| explore | - | - | - | +| explore_filled | - | - | - | +| explore_off | - | - | - | +| explore_off_filled | - | - | - | +| exposure | - | - | - | +| exposure_filled | - | - | - | +| extension | - | - | - | +| extension_filled | - | - | - | +| extension_off | - | - | - | +| extension_off_filled | - | - | - | +| face_retouching | - | - | - | +| face_retouching_filled | - | - | - | +| fact_check | - | - | - | +| fact_check_filled | - | - | - | +| fahrenheit_scale | - | - | - | +| feel_at_ease | - | - | - | +| feel_at_ease_filled | - | - | - | +| ferocious | - | - | - | +| ferocious_filled | - | - | - | +| ferris_wheel | - | - | - | +| ferris_wheel_filled | - | - | - | +| file | - | - | - | +| file_1 | - | - | - | +| file_1_filled | - | - | - | +| file_add | - | - | - | +| file_add_1 | - | - | - | +| file_add_1_filled | - | - | - | +| file_add_filled | - | - | - | +| file_attachment | - | - | - | +| file_attachment_filled | - | - | - | +| file_blocked | - | - | - | +| file_blocked_filled | - | - | - | +| file_code | - | - | - | +| file_code_1 | - | - | - | +| file_code_1_filled | - | - | - | +| file_code_filled | - | - | - | +| file_copy | - | - | - | +| file_copy_filled | - | - | - | +| file_download | - | - | - | +| file_download_filled | - | - | - | +| file_excel | - | - | - | +| file_excel_filled | - | - | - | +| file_export | - | - | - | +| file_export_filled | - | - | - | +| file_filled | - | - | - | +| file_icon | - | - | - | +| file_icon_filled | - | - | - | +| file_image | - | - | - | +| file_image_filled | - | - | - | +| file_import | - | - | - | +| file_import_filled | - | - | - | +| file_locked | - | - | - | +| file_locked_filled | - | - | - | +| file_minus | - | - | - | +| file_minus_filled | - | - | - | +| file_music | - | - | - | +| file_music_filled | - | - | - | +| file_onenote | - | - | - | +| file_onenote_filled | - | - | - | +| file_outlook | - | - | - | +| file_outlook_filled | - | - | - | +| file_paste | - | - | - | +| file_paste_filled | - | - | - | +| file_pdf | - | - | - | +| file_pdf_filled | - | - | - | +| file_powerpoint | - | - | - | +| file_powerpoint_filled | - | - | - | +| file_restore | - | - | - | +| file_restore_filled | - | - | - | +| file_safety | - | - | - | +| file_safety_filled | - | - | - | +| file_search | - | - | - | +| file_search_filled | - | - | - | +| file_setting | - | - | - | +| file_setting_filled | - | - | - | +| file_teams | - | - | - | +| file_teams_filled | - | - | - | +| file_transmit | - | - | - | +| file_transmit_double | - | - | - | +| file_transmit_double_filled | - | - | - | +| file_transmit_filled | - | - | - | +| file_unknown | - | - | - | +| file_unknown_filled | - | - | - | +| file_unlocked | - | - | - | +| file_unlocked_filled | - | - | - | +| file_word | - | - | - | +| file_word_filled | - | - | - | +| file_zip | - | - | - | +| file_zip_filled | - | - | - | +| fill_color | - | - | - | +| fill_color_1 | - | - | - | +| fill_color_1_filled | - | - | - | +| fill_color_filled | - | - | - | +| film | - | - | - | +| film_1 | - | - | - | +| film_1_filled | - | - | - | +| film_filled | - | - | - | +| filter | - | - | - | +| filter_1 | - | - | - | +| filter_1_filled | - | - | - | +| filter_2 | - | - | - | +| filter_2_filled | - | - | - | +| filter_3 | - | - | - | +| filter_3_filled | - | - | - | +| filter_clear | - | - | - | +| filter_clear_filled | - | - | - | +| filter_filled | - | - | - | +| filter_off | - | - | - | +| filter_off_filled | - | - | - | +| filter_sort | - | - | - | +| filter_sort_filled | - | - | - | +| fingerprint | - | - | - | +| fingerprint_1 | - | - | - | +| fingerprint_2 | - | - | - | +| fingerprint_3 | - | - | - | +| fish | - | - | - | +| fish_filled | - | - | - | +| flag | - | - | - | +| flag_1 | - | - | - | +| flag_1_filled | - | - | - | +| flag_2 | - | - | - | +| flag_2_filled | - | - | - | +| flag_3 | - | - | - | +| flag_3_filled | - | - | - | +| flag_4 | - | - | - | +| flag_4_filled | - | - | - | +| flag_filled | - | - | - | +| flashlight | - | - | - | +| flashlight_filled | - | - | - | +| flight_landing | - | - | - | +| flight_landing_filled | - | - | - | +| flight_takeoff | - | - | - | +| flight_takeoff_filled | - | - | - | +| flip_smiling_face | - | - | - | +| flip_smiling_face_filled | - | - | - | +| flip_to_back | - | - | - | +| flip_to_back_filled | - | - | - | +| flip_to_front | - | - | - | +| flip_to_front_filled | - | - | - | +| focus | - | - | - | +| focus_filled | - | - | - | +| fog | - | - | - | +| fog_filled | - | - | - | +| fog_night | - | - | - | +| fog_night_filled | - | - | - | +| fog_sunny | - | - | - | +| fog_sunny_filled | - | - | - | +| folder | - | - | - | +| folder_1 | - | - | - | +| folder_1_filled | - | - | - | +| folder_add | - | - | - | +| folder_add_1 | - | - | - | +| folder_add_1_filled | - | - | - | +| folder_add_filled | - | - | - | +| folder_blocked | - | - | - | +| folder_blocked_filled | - | - | - | +| folder_details | - | - | - | +| folder_details_filled | - | - | - | +| folder_export | - | - | - | +| folder_export_filled | - | - | - | +| folder_filled | - | - | - | +| folder_import | - | - | - | +| folder_import_filled | - | - | - | +| folder_locked | - | - | - | +| folder_locked_filled | - | - | - | +| folder_minus | - | - | - | +| folder_minus_filled | - | - | - | +| folder_move | - | - | - | +| folder_move_filled | - | - | - | +| folder_off | - | - | - | +| folder_off_filled | - | - | - | +| folder_open | - | - | - | +| folder_open_1 | - | - | - | +| folder_open_1_filled | - | - | - | +| folder_open_filled | - | - | - | +| folder_search | - | - | - | +| folder_search_filled | - | - | - | +| folder_setting | - | - | - | +| folder_setting_filled | - | - | - | +| folder_shared | - | - | - | +| folder_shared_filled | - | - | - | +| folder_unlocked | - | - | - | +| folder_unlocked_filled | - | - | - | +| folder_zip | - | - | - | +| folder_zip_filled | - | - | - | +| forest | - | - | - | +| forest_filled | - | - | - | +| fork | - | - | - | +| fork_filled | - | - | - | +| form | - | - | - | +| form_filled | - | - | - | +| format_horizontal_align_bottom | - | - | - | +| format_horizontal_align_center | - | - | - | +| format_horizontal_align_top | - | - | - | +| format_vertical_align_center | - | - | - | +| format_vertical_align_left | - | - | - | +| format_vertical_align_right | - | - | - | +| forward | - | - | - | +| forward_filled | - | - | - | +| frame | - | - | - | +| frame_1 | - | - | - | +| frame_1_filled | - | - | - | +| frame_filled | - | - | - | +| fries | - | - | - | +| fries_filled | - | - | - | +| fullscreen | - | - | - | +| fullscreen_1 | - | - | - | +| fullscreen_2 | - | - | - | +| fullscreen_exit | - | - | - | +| fullscreen_exit_1 | - | - | - | +| function_curve | - | - | - | +| functions | - | - | - | +| functions_1 | - | - | - | +| gamepad | - | - | - | +| gamepad_1 | - | - | - | +| gamepad_1_filled | - | - | - | +| gamepad_filled | - | - | - | +| gamma | - | - | - | +| garlic | - | - | - | +| garlic_filled | - | - | - | +| gender_female | - | - | - | +| gender_male | - | - | - | +| gesture_applause | - | - | - | +| gesture_applause_filled | - | - | - | +| gesture_click | - | - | - | +| gesture_click_filled | - | - | - | +| gesture_down | - | - | - | +| gesture_down_filled | - | - | - | +| gesture_expansion | - | - | - | +| gesture_expansion_filled | - | - | - | +| gesture_left | - | - | - | +| gesture_left_filled | - | - | - | +| gesture_left_slip | - | - | - | +| gesture_left_slip_filled | - | - | - | +| gesture_open | - | - | - | +| gesture_open_filled | - | - | - | +| gesture_pray | - | - | - | +| gesture_pray_filled | - | - | - | +| gesture_press | - | - | - | +| gesture_press_filled | - | - | - | +| gesture_ranslation | - | - | - | +| gesture_ranslation_filled | - | - | - | +| gesture_right | - | - | - | +| gesture_right_filled | - | - | - | +| gesture_right_slip | - | - | - | +| gesture_right_slip_filled | - | - | - | +| gesture_slide_left_and_right | - | - | - | +| gesture_slide_left_and_right_filled | - | - | - | +| gesture_slide_up | - | - | - | +| gesture_slide_up_filled | - | - | - | +| gesture_typing | - | - | - | +| gesture_typing_filled | - | - | - | +| gesture_up | - | - | - | +| gesture_up_and_down | - | - | - | +| gesture_up_and_down_filled | - | - | - | +| gesture_up_filled | - | - | - | +| gesture_wipe_down | - | - | - | +| gesture_wipe_down_filled | - | - | - | +| gift | - | - | - | +| gift_filled | - | - | - | +| giggle | - | - | - | +| giggle_filled | - | - | - | +| git_branch | - | - | - | +| git_branch_filled | - | - | - | +| git_commit | - | - | - | +| git_commit_filled | - | - | - | +| git_merge | - | - | - | +| git_merge_filled | - | - | - | +| git_pull_request | - | - | - | +| git_pull_request_filled | - | - | - | +| git_repository | - | - | - | +| git_repository_commits | - | - | - | +| git_repository_commits_filled | - | - | - | +| git_repository_filled | - | - | - | +| git_repository_private | - | - | - | +| git_repository_private_filled | - | - | - | +| gps | - | - | - | +| gps_filled | - | - | - | +| grape | - | - | - | +| grape_filled | - | - | - | +| greater_than | - | - | - | +| greater_than_or_equal | - | - | - | +| green_onion | - | - | - | +| grid_add | - | - | - | +| grid_add_filled | - | - | - | +| grid_view | - | - | - | +| grid_view_filled | - | - | - | +| guitar | - | - | - | +| guitar_filled | - | - | - | +| hamburger | - | - | - | +| hamburger_filled | - | - | - | +| happy | - | - | - | +| happy_filled | - | - | - | +| hard_disk_storage | - | - | - | +| hard_disk_storage_filled | - | - | - | +| hard_drive | - | - | - | +| hard_drive_filled | - | - | - | +| hashtag | - | - | - | +| hd | - | - | - | +| hd_filled | - | - | - | +| heart | - | - | - | +| heart_filled | - | - | - | +| help | - | - | - | +| help_circle | - | - | - | +| help_circle_filled | - | - | - | +| help_rectangle | - | - | - | +| help_rectangle_filled | - | - | - | +| highlight | - | - | - | +| highlight_1 | - | - | - | +| highlight_1_filled | - | - | - | +| history | - | - | - | +| history_setting | - | - | - | +| home | - | - | - | +| home_filled | - | - | - | +| horizontal | - | - | - | +| horizontal_filled | - | - | - | +| hospital | - | - | - | +| hospital_1 | - | - | - | +| hospital_1_filled | - | - | - | +| hospital_filled | - | - | - | +| hotspot_wave | - | - | - | +| hotspot_wave_filled | - | - | - | +| hourglass | - | - | - | +| hourglass_filled | - | - | - | +| houses | - | - | - | +| houses_1 | - | - | - | +| houses_1_filled | - | - | - | +| houses_2 | - | - | - | +| houses_2_filled | - | - | - | +| houses_filled | - | - | - | +| html5 | - | - | - | +| html5_filled | - | - | - | +| https | - | - | - | +| https_filled | - | - | - | +| ice_cream | - | - | - | +| ice_cream_filled | - | - | - | +| icon | - | - | - | +| icon_filled | - | - | - | +| image | - | - | - | +| image_1 | - | - | - | +| image_1_filled | - | - | - | +| image_add | - | - | - | +| image_add_filled | - | - | - | +| image_edit | - | - | - | +| image_edit_filled | - | - | - | +| image_error | - | - | - | +| image_error_filled | - | - | - | +| image_filled | - | - | - | +| image_off | - | - | - | +| image_off_filled | - | - | - | +| image_search | - | - | - | +| image_search_filled | - | - | - | +| indent_left | - | - | - | +| indent_right | - | - | - | +| indicator | - | - | - | +| indicator_filled | - | - | - | +| info_circle | - | - | - | +| info_circle_filled | - | - | - | +| ink | - | - | - | +| ink_filled | - | - | - | +| install | - | - | - | +| install_desktop | - | - | - | +| install_desktop_filled | - | - | - | +| install_filled | - | - | - | +| install_mobile | - | - | - | +| install_mobile_filled | - | - | - | +| institution | - | - | - | +| institution_checked | - | - | - | +| institution_checked_filled | - | - | - | +| institution_filled | - | - | - | +| internet | - | - | - | +| internet_filled | - | - | - | +| ipod | - | - | - | +| ipod_filled | - | - | - | +| joyful | - | - | - | +| joyful_filled | - | - | - | +| jump | - | - | - | +| jump_double | - | - | - | +| jump_off | - | - | - | +| key | - | - | - | +| key_filled | - | - | - | +| keyboard | - | - | - | +| keyboard_filled | - | - | - | +| laptop | - | - | - | +| laptop_filled | - | - | - | +| layers | - | - | - | +| layers_filled | - | - | - | +| layout | - | - | - | +| layout_filled | - | - | - | +| leaderboard | - | - | - | +| leaderboard_filled | - | - | - | +| lemon | - | - | - | +| lemon_filled | - | - | - | +| lemon_slice | - | - | - | +| lemon_slice_filled | - | - | - | +| less_than | - | - | - | +| less_than_or_equal | - | - | - | +| letters_a | - | - | - | +| letters_b | - | - | - | +| letters_c | - | - | - | +| letters_d | - | - | - | +| letters_e | - | - | - | +| letters_f | - | - | - | +| letters_g | - | - | - | +| letters_h | - | - | - | +| letters_i | - | - | - | +| letters_j | - | - | - | +| letters_k | - | - | - | +| letters_l | - | - | - | +| letters_m | - | - | - | +| letters_n | - | - | - | +| letters_o | - | - | - | +| letters_p | - | - | - | +| letters_q | - | - | - | +| letters_r | - | - | - | +| letters_s | - | - | - | +| letters_t | - | - | - | +| letters_u | - | - | - | +| letters_v | - | - | - | +| letters_w | - | - | - | +| letters_x | - | - | - | +| letters_y | - | - | - | +| letters_z | - | - | - | +| lightbulb | - | - | - | +| lightbulb_circle | - | - | - | +| lightbulb_circle_filled | - | - | - | +| lightbulb_filled | - | - | - | +| lighthouse | - | - | - | +| lighthouse_1 | - | - | - | +| lighthouse_1_filled | - | - | - | +| lighthouse_2 | - | - | - | +| lighthouse_2_filled | - | - | - | +| lighthouse_filled | - | - | - | +| lighting_circle | - | - | - | +| lighting_circle_filled | - | - | - | +| line_height | - | - | - | +| link | - | - | - | +| link_1 | - | - | - | +| link_unlink | - | - | - | +| liquor | - | - | - | +| liquor_filled | - | - | - | +| list | - | - | - | +| list_numbered | - | - | - | +| load | - | - | - | +| loading | - | - | - | +| location | - | - | - | +| location_1 | - | - | - | +| location_1_filled | - | - | - | +| location_enlargement | - | - | - | +| location_enlargement_filled | - | - | - | +| location_error | - | - | - | +| location_error_filled | - | - | - | +| location_filled | - | - | - | +| location_parking_place | - | - | - | +| location_parking_place_filled | - | - | - | +| location_reduction | - | - | - | +| location_reduction_filled | - | - | - | +| location_setting | - | - | - | +| location_setting_filled | - | - | - | +| lock_off | - | - | - | +| lock_off_filled | - | - | - | +| lock_on | - | - | - | +| lock_on_filled | - | - | - | +| lock_time | - | - | - | +| lock_time_filled | - | - | - | +| login | - | - | - | +| logo_adobe_illustrate | - | - | - | +| logo_adobe_illustrate_filled | - | - | - | +| logo_adobe_lightroom | - | - | - | +| logo_adobe_lightroom_filled | - | - | - | +| logo_adobe_photoshop | - | - | - | +| logo_adobe_photoshop_filled | - | - | - | +| logo_android | - | - | - | +| logo_android_filled | - | - | - | +| logo_apple | - | - | - | +| logo_apple_filled | - | - | - | +| logo_behance | - | - | - | +| logo_chrome | - | - | - | +| logo_chrome_filled | - | - | - | +| logo_cinema4d | - | - | - | +| logo_cinema4d_filled | - | - | - | +| logo_codepen | - | - | - | +| logo_codesandbox | - | - | - | +| logo_dribbble | - | - | - | +| logo_dribbble_filled | - | - | - | +| logo_facebook | - | - | - | +| logo_facebook_filled | - | - | - | +| logo_figma | - | - | - | +| logo_figma_filled | - | - | - | +| logo_framer | - | - | - | +| logo_framer_filled | - | - | - | +| logo_github | - | - | - | +| logo_github_filled | - | - | - | +| logo_gitlab | - | - | - | +| logo_gitlab_filled | - | - | - | +| logo_ie | - | - | - | +| logo_ie_filled | - | - | - | +| logo_instagram | - | - | - | +| logo_instagram_filled | - | - | - | +| logo_qq | - | - | - | +| logo_qq_filled | - | - | - | +| logo_twitter | - | - | - | +| logo_twitter_filled | - | - | - | +| logo_wechat_stroke | - | - | - | +| logo_wechat_stroke_filled | - | - | - | +| logo_wechatpay | - | - | - | +| logo_wechatpay_filled | - | - | - | +| logo_wecom | - | - | - | +| logo_wecom_filled | - | - | - | +| logo_windows | - | - | - | +| logo_windows_filled | - | - | - | +| logo_youtube | - | - | - | +| logo_youtube_filled | - | - | - | +| logout | - | - | - | +| look_around | - | - | - | +| look_around_filled | - | - | - | +| loudspeaker | - | - | - | +| loudspeaker_filled | - | - | - | +| mail | - | - | - | +| mail_filled | - | - | - | +| map | - | - | - | +| map_3d | - | - | - | +| map_3d_filled | - | - | - | +| map_add | - | - | - | +| map_add_filled | - | - | - | +| map_aiming | - | - | - | +| map_aiming_filled | - | - | - | +| map_blocked | - | - | - | +| map_blocked_filled | - | - | - | +| map_bubble | - | - | - | +| map_bubble_filled | - | - | - | +| map_cancel | - | - | - | +| map_cancel_filled | - | - | - | +| map_chat | - | - | - | +| map_chat_filled | - | - | - | +| map_checked | - | - | - | +| map_checked_filled | - | - | - | +| map_collection | - | - | - | +| map_collection_filled | - | - | - | +| map_connection | - | - | - | +| map_connection_filled | - | - | - | +| map_distance | - | - | - | +| map_distance_filled | - | - | - | +| map_double | - | - | - | +| map_double_filled | - | - | - | +| map_edit | - | - | - | +| map_edit_filled | - | - | - | +| map_filled | - | - | - | +| map_grid | - | - | - | +| map_grid_filled | - | - | - | +| map_information | - | - | - | +| map_information_1 | - | - | - | +| map_information_1_filled | - | - | - | +| map_information_2 | - | - | - | +| map_information_2_filled | - | - | - | +| map_information_filled | - | - | - | +| map_location | - | - | - | +| map_location_filled | - | - | - | +| map_locked | - | - | - | +| map_locked_filled | - | - | - | +| map_marked | - | - | - | +| map_marked_filled | - | - | - | +| map_navigation | - | - | - | +| map_navigation_filled | - | - | - | +| map_outline | - | - | - | +| map_outline_filled | - | - | - | +| map_route_planning | - | - | - | +| map_route_planning_filled | - | - | - | +| map_ruler | - | - | - | +| map_ruler_filled | - | - | - | +| map_safety | - | - | - | +| map_safety_filled | - | - | - | +| map_search | - | - | - | +| map_search_1 | - | - | - | +| map_search_1_filled | - | - | - | +| map_search_filled | - | - | - | +| map_setting | - | - | - | +| map_setting_filled | - | - | - | +| map_unlocked | - | - | - | +| map_unlocked_filled | - | - | - | +| mark_as_unread | - | - | - | +| mark_as_unread_filled | - | - | - | +| markup | - | - | - | +| markup_filled | - | - | - | +| mathematics | - | - | - | +| mathematics_filled | - | - | - | +| measurement | - | - | - | +| measurement_1 | - | - | - | +| measurement_1_filled | - | - | - | +| measurement_2 | - | - | - | +| measurement_2_filled | - | - | - | +| measurement_filled | - | - | - | +| meat_pepper | - | - | - | +| meat_pepper_filled | - | - | - | +| media_library | - | - | - | +| media_library_filled | - | - | - | +| member | - | - | - | +| member_filled | - | - | - | +| menu | - | - | - | +| menu_application | - | - | - | +| menu_filled | - | - | - | +| menu_fold | - | - | - | +| menu_unfold | - | - | - | +| merge_cells | - | - | - | +| merge_cells_filled | - | - | - | +| microphone | - | - | - | +| microphone_1 | - | - | - | +| microphone_1_filled | - | - | - | +| microphone_2 | - | - | - | +| microphone_2_filled | - | - | - | +| microphone_filled | - | - | - | +| milk | - | - | - | +| milk_filled | - | - | - | +| minus | - | - | - | +| minus_circle | - | - | - | +| minus_circle_filled | - | - | - | +| minus_rectangle | - | - | - | +| minus_rectangle_filled | - | - | - | +| mirror | - | - | - | +| mirror_filled | - | - | - | +| mobile | - | - | - | +| mobile_blocked | - | - | - | +| mobile_blocked_filled | - | - | - | +| mobile_filled | - | - | - | +| mobile_list | - | - | - | +| mobile_list_filled | - | - | - | +| mobile_navigation | - | - | - | +| mobile_navigation_filled | - | - | - | +| mobile_shortcut | - | - | - | +| mobile_shortcut_filled | - | - | - | +| mobile_vibrate | - | - | - | +| mobile_vibrate_filled | - | - | - | +| mode_dark | - | - | - | +| mode_dark_filled | - | - | - | +| mode_light | - | - | - | +| mode_light_filled | - | - | - | +| module | - | - | - | +| module_filled | - | - | - | +| money | - | - | - | +| money_filled | - | - | - | +| monument | - | - | - | +| monument_filled | - | - | - | +| moon | - | - | - | +| moon_fall | - | - | - | +| moon_fall_filled | - | - | - | +| moon_filled | - | - | - | +| moon_rising | - | - | - | +| moon_rising_filled | - | - | - | +| more | - | - | - | +| mosque | - | - | - | +| mosque_1 | - | - | - | +| mosque_1_filled | - | - | - | +| mosque_filled | - | - | - | +| mouse | - | - | - | +| mouse_filled | - | - | - | +| move | - | - | - | +| move_1 | - | - | - | +| movie_clapper | - | - | - | +| movie_clapper_filled | - | - | - | +| multiply | - | - | - | +| museum | - | - | - | +| museum_1 | - | - | - | +| museum_1_filled | - | - | - | +| museum_2 | - | - | - | +| museum_2_filled | - | - | - | +| museum_filled | - | - | - | +| mushroom | - | - | - | +| mushroom_1 | - | - | - | +| mushroom_1_filled | - | - | - | +| mushroom_filled | - | - | - | +| music | - | - | - | +| music_1 | - | - | - | +| music_1_filled | - | - | - | +| music_2 | - | - | - | +| music_2_filled | - | - | - | +| music_filled | - | - | - | +| music_rectangle_add | - | - | - | +| music_rectangle_add_filled | - | - | - | +| navigation_arrow | - | - | - | +| navigation_arrow_filled | - | - | - | +| next | - | - | - | +| next_filled | - | - | - | +| no_expression | - | - | - | +| no_expression_filled | - | - | - | +| noodle | - | - | - | +| noodle_filled | - | - | - | +| notification | - | - | - | +| notification_add | - | - | - | +| notification_add_filled | - | - | - | +| notification_circle | - | - | - | +| notification_circle_filled | - | - | - | +| notification_error | - | - | - | +| notification_error_filled | - | - | - | +| notification_filled | - | - | - | +| numbers_0 | - | - | - | +| numbers_0_1 | - | - | - | +| numbers_1 | - | - | - | +| numbers_1_1 | - | - | - | +| numbers_2 | - | - | - | +| numbers_2_1 | - | - | - | +| numbers_3 | - | - | - | +| numbers_3_1 | - | - | - | +| numbers_4 | - | - | - | +| numbers_4_1 | - | - | - | +| numbers_5 | - | - | - | +| numbers_5_1 | - | - | - | +| numbers_6 | - | - | - | +| numbers_6_1 | - | - | - | +| numbers_7 | - | - | - | +| numbers_7_1 | - | - | - | +| numbers_8 | - | - | - | +| numbers_8_1 | - | - | - | +| numbers_9 | - | - | - | +| numbers_9_1 | - | - | - | +| nut | - | - | - | +| nut_filled | - | - | - | +| object_storage | - | - | - | +| open_mouth | - | - | - | +| open_mouth_filled | - | - | - | +| opera | - | - | - | +| opera_filled | - | - | - | +| order_adjustment_column | - | - | - | +| order_ascending | - | - | - | +| order_descending | - | - | - | +| outbox | - | - | - | +| outbox_filled | - | - | - | +| page_first | - | - | - | +| page_head | - | - | - | +| page_head_filled | - | - | - | +| page_last | - | - | - | +| palace | - | - | - | +| palace_1 | - | - | - | +| palace_1_filled | - | - | - | +| palace_2 | - | - | - | +| palace_2_filled | - | - | - | +| palace_3 | - | - | - | +| palace_3_filled | - | - | - | +| palace_4 | - | - | - | +| palace_4_filled | - | - | - | +| palace_filled | - | - | - | +| palette | - | - | - | +| palette_1 | - | - | - | +| palette_1_filled | - | - | - | +| palette_filled | - | - | - | +| panorama_horizontal | - | - | - | +| panorama_horizontal_filled | - | - | - | +| panorama_vertical | - | - | - | +| panorama_vertical_filled | - | - | - | +| pantone | - | - | - | +| pantone_filled | - | - | - | +| parabola | - | - | - | +| parentheses | - | - | - | +| paste | - | - | - | +| paste_filled | - | - | - | +| patio | - | - | - | +| patio_filled | - | - | - | +| pause | - | - | - | +| pause_circle | - | - | - | +| pause_circle_filled | - | - | - | +| pause_circle_stroke | - | - | - | +| pause_circle_stroke_filled | - | - | - | +| pea | - | - | - | +| pea_filled | - | - | - | +| peach | - | - | - | +| peach_filled | - | - | - | +| pear | - | - | - | +| pear_filled | - | - | - | +| pearl_of_the_orient | - | - | - | +| pearl_of_the_orient_filled | - | - | - | +| pen | - | - | - | +| pen_ball | - | - | - | +| pen_ball_filled | - | - | - | +| pen_brush | - | - | - | +| pen_brush_filled | - | - | - | +| pen_filled | - | - | - | +| pen_mark | - | - | - | +| pen_mark_filled | - | - | - | +| pen_quill | - | - | - | +| pen_quill_filled | - | - | - | +| pending | - | - | - | +| pending_filled | - | - | - | +| percent | - | - | - | +| personal_information | - | - | - | +| personal_information_filled | - | - | - | +| phone_locked | - | - | - | +| phone_locked_filled | - | - | - | +| phone_search | - | - | - | +| phone_search_filled | - | - | - | +| pi | - | - | - | +| piano | - | - | - | +| piano_filled | - | - | - | +| pin | - | - | - | +| pin_filled | - | - | - | +| play | - | - | - | +| play_circle | - | - | - | +| play_circle_filled | - | - | - | +| play_circle_stroke | - | - | - | +| play_circle_stroke_add | - | - | - | +| play_circle_stroke_add_filled | - | - | - | +| play_circle_stroke_filled | - | - | - | +| play_demo | - | - | - | +| play_demo_filled | - | - | - | +| play_rectangle | - | - | - | +| play_rectangle_filled | - | - | - | +| plus | - | - | - | +| popsicle | - | - | - | +| popsicle_filled | - | - | - | +| portrait | - | - | - | +| portrait_filled | - | - | - | +| pout | - | - | - | +| pout_filled | - | - | - | +| poweroff | - | - | - | +| precise_monitor | - | - | - | +| previous | - | - | - | +| previous_filled | - | - | - | +| print | - | - | - | +| print_filled | - | - | - | +| pumpkin | - | - | - | +| pumpkin_filled | - | - | - | +| pyramid | - | - | - | +| pyramid_filled | - | - | - | +| pyramid_maya | - | - | - | +| pyramid_maya_filled | - | - | - | +| qrcode | - | - | - | +| quadratic | - | - | - | +| questionnaire | - | - | - | +| questionnaire_double | - | - | - | +| questionnaire_double_filled | - | - | - | +| questionnaire_filled | - | - | - | +| queue | - | - | - | +| queue_filled | - | - | - | +| radar | - | - | - | +| radio_1 | - | - | - | +| radio_1_filled | - | - | - | +| radio_2 | - | - | - | +| radio_2_filled | - | - | - | +| radish | - | - | - | +| radish_filled | - | - | - | +| rain_heavy | - | - | - | +| rain_light | - | - | - | +| rain_light_filled | - | - | - | +| rain_medium | - | - | - | +| rainbow | - | - | - | +| rectangle | - | - | - | +| rectangle_filled | - | - | - | +| refresh | - | - | - | +| relation | - | - | - | +| relativity | - | - | - | +| relativity_filled | - | - | - | +| remote_wave | - | - | - | +| remote_wave_filled | - | - | - | +| remove | - | - | - | +| replay | - | - | - | +| replay_filled | - | - | - | +| rice | - | - | - | +| rice_ball | - | - | - | +| rice_ball_filled | - | - | - | +| rice_filled | - | - | - | +| roast | - | - | - | +| roast_filled | - | - | - | +| rocket | - | - | - | +| rocket_filled | - | - | - | +| rollback | - | - | - | +| rollfront | - | - | - | +| root_list | - | - | - | +| root_list_filled | - | - | - | +| rotate | - | - | - | +| rotate_locked | - | - | - | +| rotate_locked_filled | - | - | - | +| rotation | - | - | - | +| round | - | - | - | +| round_filled | - | - | - | +| router_wave | - | - | - | +| router_wave_filled | - | - | - | +| rss | - | - | - | +| ruler | - | - | - | +| ruler_filled | - | - | - | +| sailing_hotel | - | - | - | +| sailing_hotel_filled | - | - | - | +| sandwich | - | - | - | +| sandwich_filled | - | - | - | +| saturation | - | - | - | +| saturation_filled | - | - | - | +| sausage | - | - | - | +| sausage_filled | - | - | - | +| save | - | - | - | +| save_filled | - | - | - | +| saving_pot | - | - | - | +| saving_pot_filled | - | - | - | +| scan | - | - | - | +| screen_4k | - | - | - | +| screen_4k_filled | - | - | - | +| screencast | - | - | - | +| screencast_filled | - | - | - | +| screenshot | - | - | - | +| scroll_bar | - | - | - | +| scroll_bar_filled | - | - | - | +| sd_card | - | - | - | +| sd_card_1 | - | - | - | +| sd_card_1_filled | - | - | - | +| sd_card_filled | - | - | - | +| search | - | - | - | +| search_error | - | - | - | +| search_error_filled | - | - | - | +| search_filled | - | - | - | +| secured | - | - | - | +| secured_filled | - | - | - | +| send | - | - | - | +| send_cancel | - | - | - | +| send_cancel_filled | - | - | - | +| send_filled | - | - | - | +| sensors | - | - | - | +| sensors_1 | - | - | - | +| sensors_2 | - | - | - | +| sensors_off | - | - | - | +| sequence | - | - | - | +| sequence_filled | - | - | - | +| serenity | - | - | - | +| serenity_filled | - | - | - | +| server | - | - | - | +| server_filled | - | - | - | +| service | - | - | - | +| service_filled | - | - | - | +| setting | - | - | - | +| setting_1 | - | - | - | +| setting_1_filled | - | - | - | +| setting_filled | - | - | - | +| share | - | - | - | +| share_1 | - | - | - | +| share_1_filled | - | - | - | +| share_filled | - | - | - | +| sharpness | - | - | - | +| sharpness_filled | - | - | - | +| shield_error | - | - | - | +| shield_error_filled | - | - | - | +| shimen | - | - | - | +| shimen_filled | - | - | - | +| shop | - | - | - | +| shop_1 | - | - | - | +| shop_1_filled | - | - | - | +| shop_2 | - | - | - | +| shop_2_filled | - | - | - | +| shop_3 | - | - | - | +| shop_3_filled | - | - | - | +| shop_4 | - | - | - | +| shop_4_filled | - | - | - | +| shop_5 | - | - | - | +| shop_5_filled | - | - | - | +| shop_filled | - | - | - | +| shrimp | - | - | - | +| shrimp_filled | - | - | - | +| shrink_horizontal | - | - | - | +| shrink_vertical | - | - | - | +| shutter | - | - | - | +| shutter_filled | - | - | - | +| shutup | - | - | - | +| shutup_filled | - | - | - | +| sim_card | - | - | - | +| sim_card_1 | - | - | - | +| sim_card_1_filled | - | - | - | +| sim_card_2 | - | - | - | +| sim_card_2_filled | - | - | - | +| sim_card_filled | - | - | - | +| sinister_smile | - | - | - | +| sinister_smile_filled | - | - | - | +| sip | - | - | - | +| sip_filled | - | - | - | +| sitemap | - | - | - | +| sitemap_filled | - | - | - | +| slash | - | - | - | +| sleep | - | - | - | +| sleep_filled | - | - | - | +| slice | - | - | - | +| slice_filled | - | - | - | +| slideshow | - | - | - | +| slideshow_filled | - | - | - | +| smile | - | - | - | +| smile_filled | - | - | - | +| sneer | - | - | - | +| sneer_filled | - | - | - | +| snowflake | - | - | - | +| sonic | - | - | - | +| sound | - | - | - | +| sound_down | - | - | - | +| sound_down_filled | - | - | - | +| sound_filled | - | - | - | +| sound_high | - | - | - | +| sound_high_filled | - | - | - | +| sound_low | - | - | - | +| sound_low_filled | - | - | - | +| sound_mute | - | - | - | +| sound_mute_1 | - | - | - | +| sound_mute_1_filled | - | - | - | +| sound_mute_filled | - | - | - | +| sound_up | - | - | - | +| sound_up_filled | - | - | - | +| space | - | - | - | +| speechless | - | - | - | +| speechless_1 | - | - | - | +| speechless_1_filled | - | - | - | +| speechless_filled | - | - | - | +| star | - | - | - | +| star_filled | - | - | - | +| statue_of_jesus | - | - | - | +| statue_of_jesus_filled | - | - | - | +| sticky_note | - | - | - | +| sticky_note_filled | - | - | - | +| stop | - | - | - | +| stop_circle | - | - | - | +| stop_circle_filled | - | - | - | +| stop_circle_stroke | - | - | - | +| stop_circle_stroke_filled | - | - | - | +| store | - | - | - | +| store_filled | - | - | - | +| street_road | - | - | - | +| street_road_1 | - | - | - | +| street_road_1_filled | - | - | - | +| street_road_filled | - | - | - | +| subtitle | - | - | - | +| subtitle_filled | - | - | - | +| subway_line | - | - | - | +| subway_line_filled | - | - | - | +| sum | - | - | - | +| sun_fall | - | - | - | +| sun_fall_filled | - | - | - | +| sun_rising | - | - | - | +| sun_rising_filled | - | - | - | +| sunny | - | - | - | +| sunny_filled | - | - | - | +| support | - | - | - | +| support_filled | - | - | - | +| surprised | - | - | - | +| surprised_1 | - | - | - | +| surprised_1_filled | - | - | - | +| surprised_filled | - | - | - | +| swap | - | - | - | +| swap_left | - | - | - | +| swap_right | - | - | - | +| swear_1 | - | - | - | +| swear_1_filled | - | - | - | +| swear_2 | - | - | - | +| swear_2_filled | - | - | - | +| system_2 | - | - | - | +| system_3 | - | - | - | +| system_3_filled | - | - | - | +| system_application | - | - | - | +| system_application_filled | - | - | - | +| system_blocked | - | - | - | +| system_blocked_filled | - | - | - | +| system_code | - | - | - | +| system_code_filled | - | - | - | +| system_components | - | - | - | +| system_components_filled | - | - | - | +| system_coordinate | - | - | - | +| system_coordinate_filled | - | - | - | +| system_device | - | - | - | +| system_device_filled | - | - | - | +| system_interface | - | - | - | +| system_interface_filled | - | - | - | +| system_location | - | - | - | +| system_location_filled | - | - | - | +| system_locked | - | - | - | +| system_locked_filled | - | - | - | +| system_log | - | - | - | +| system_log_filled | - | - | - | +| system_marked | - | - | - | +| system_marked_filled | - | - | - | +| system_messages | - | - | - | +| system_messages_filled | - | - | - | +| system_regulation | - | - | - | +| system_regulation_filled | - | - | - | +| system_search | - | - | - | +| system_search_filled | - | - | - | +| system_setting | - | - | - | +| system_setting_filled | - | - | - | +| system_storage | - | - | - | +| system_storage_filled | - | - | - | +| system_sum | - | - | - | +| system_unlocked | - | - | - | +| system_unlocked_filled | - | - | - | +| tab | - | - | - | +| tab_filled | - | - | - | +| table | - | - | - | +| table_1 | - | - | - | +| table_1_filled | - | - | - | +| table_2 | - | - | - | +| table_2_filled | - | - | - | +| table_add | - | - | - | +| table_add_filled | - | - | - | +| table_filled | - | - | - | +| table_split | - | - | - | +| table_split_filled | - | - | - | +| tag | - | - | - | +| tag_filled | - | - | - | +| tangerinr | - | - | - | +| tangerinr_filled | - | - | - | +| tape | - | - | - | +| tape_filled | - | - | - | +| task | - | - | - | +| task_1 | - | - | - | +| task_1_filled | - | - | - | +| task_add | - | - | - | +| task_add_1 | - | - | - | +| task_add_filled | - | - | - | +| task_checked | - | - | - | +| task_checked_1 | - | - | - | +| task_checked_filled | - | - | - | +| task_double | - | - | - | +| task_double_filled | - | - | - | +| task_error | - | - | - | +| task_error_filled | - | - | - | +| task_filled | - | - | - | +| task_location | - | - | - | +| task_location_filled | - | - | - | +| task_marked | - | - | - | +| task_marked_filled | - | - | - | +| task_setting | - | - | - | +| task_setting_filled | - | - | - | +| task_time | - | - | - | +| task_time_filled | - | - | - | +| task_visible | - | - | - | +| task_visible_filled | - | - | - | +| tea | - | - | - | +| tea_filled | - | - | - | +| teahouse | - | - | - | +| teahouse_filled | - | - | - | +| template | - | - | - | +| template_filled | - | - | - | +| temple | - | - | - | +| temple_filled | - | - | - | +| terminal | - | - | - | +| terminal_rectangle | - | - | - | +| terminal_rectangle_1 | - | - | - | +| terminal_rectangle_1_filled | - | - | - | +| terminal_rectangle_filled | - | - | - | +| terminal_window | - | - | - | +| terminal_window_filled | - | - | - | +| textbox | - | - | - | +| textbox_filled | - | - | - | +| textformat_bold | - | - | - | +| textformat_color | - | - | - | +| textformat_italic | - | - | - | +| textformat_strikethrough | - | - | - | +| textformat_underline | - | - | - | +| textformat_wrap | - | - | - | +| theaters | - | - | - | +| theaters_filled | - | - | - | +| thumb_down | - | - | - | +| thumb_down_1 | - | - | - | +| thumb_down_1_filled | - | - | - | +| thumb_down_2 | - | - | - | +| thumb_down_2_filled | - | - | - | +| thumb_down_filled | - | - | - | +| thumb_up | - | - | - | +| thumb_up_1 | - | - | - | +| thumb_up_1_filled | - | - | - | +| thumb_up_2 | - | - | - | +| thumb_up_2_filled | - | - | - | +| thumb_up_filled | - | - | - | +| thunder | - | - | - | +| thunderstorm | - | - | - | +| thunderstorm_night | - | - | - | +| thunderstorm_night_filled | - | - | - | +| thunderstorm_sunny | - | - | - | +| thunderstorm_sunny_filled | - | - | - | +| ticket | - | - | - | +| ticket_filled | - | - | - | +| time | - | - | - | +| time_filled | - | - | - | +| tips | - | - | - | +| tips_double | - | - | - | +| tips_double_filled | - | - | - | +| tips_filled | - | - | - | +| tomato | - | - | - | +| tomato_filled | - | - | - | +| tools | - | - | - | +| tools_circle | - | - | - | +| tools_circle_filled | - | - | - | +| tools_filled | - | - | - | +| tornado | - | - | - | +| tower | - | - | - | +| tower_1 | - | - | - | +| tower_1_filled | - | - | - | +| tower_2 | - | - | - | +| tower_2_filled | - | - | - | +| tower_3 | - | - | - | +| tower_3_filled | - | - | - | +| tower_clock | - | - | - | +| tower_clock_filled | - | - | - | +| tower_filled | - | - | - | +| town | - | - | - | +| town_filled | - | - | - | +| traffic | - | - | - | +| traffic_events | - | - | - | +| traffic_events_filled | - | - | - | +| traffic_filled | - | - | - | +| transform | - | - | - | +| transform_1 | - | - | - | +| transform_1_filled | - | - | - | +| transform_2 | - | - | - | +| transform_3 | - | - | - | +| transform_filled | - | - | - | +| translate | - | - | - | +| translate_1 | - | - | - | +| tree_round_dot | - | - | - | +| tree_round_dot_filled | - | - | - | +| tree_round_dot_vertical | - | - | - | +| tree_round_dot_vertical_filled | - | - | - | +| tree_square_dot | - | - | - | +| tree_square_dot_filled | - | - | - | +| tree_square_dot_vertical | - | - | - | +| tree_square_dot_vertical_filled | - | - | - | +| trending_down | - | - | - | +| trending_up | - | - | - | +| tv | - | - | - | +| tv_1 | - | - | - | +| tv_1_filled | - | - | - | +| tv_2 | - | - | - | +| tv_2_filled | - | - | - | +| tv_filled | - | - | - | +| typography | - | - | - | +| typography_filled | - | - | - | +| uncomfortable | - | - | - | +| uncomfortable_1 | - | - | - | +| uncomfortable_1_filled | - | - | - | +| uncomfortable_2 | - | - | - | +| uncomfortable_2_filled | - | - | - | +| uncomfortable_filled | - | - | - | +| undertake | - | - | - | +| undertake_delivery | - | - | - | +| undertake_delivery_filled | - | - | - | +| undertake_environment_protection | - | - | - | +| undertake_environment_protection_filled | - | - | - | +| undertake_filled | - | - | - | +| undertake_hold_up | - | - | - | +| undertake_hold_up_filled | - | - | - | +| undertake_transaction | - | - | - | +| undertake_transaction_filled | - | - | - | +| unfold_less | - | - | - | +| unfold_more | - | - | - | +| unhappy | - | - | - | +| unhappy_1 | - | - | - | +| unhappy_1_filled | - | - | - | +| unhappy_filled | - | - | - | +| uninstall | - | - | - | +| uninstall_filled | - | - | - | +| upload | - | - | - | +| upload_1 | - | - | - | +| upscale | - | - | - | +| usb | - | - | - | +| usb_filled | - | - | - | +| user | - | - | - | +| user_1 | - | - | - | +| user_1_filled | - | - | - | +| user_add | - | - | - | +| user_add_filled | - | - | - | +| user_arrow_down | - | - | - | +| user_arrow_down_filled | - | - | - | +| user_arrow_left | - | - | - | +| user_arrow_left_filled | - | - | - | +| user_arrow_right | - | - | - | +| user_arrow_right_filled | - | - | - | +| user_arrow_up | - | - | - | +| user_arrow_up_filled | - | - | - | +| user_avatar | - | - | - | +| user_avatar_filled | - | - | - | +| user_blocked | - | - | - | +| user_blocked_filled | - | - | - | +| user_business | - | - | - | +| user_business_filled | - | - | - | +| user_checked | - | - | - | +| user_checked_1 | - | - | - | +| user_checked_1_filled | - | - | - | +| user_checked_filled | - | - | - | +| user_circle | - | - | - | +| user_circle_filled | - | - | - | +| user_clear | - | - | - | +| user_clear_filled | - | - | - | +| user_error_1 | - | - | - | +| user_error_1_filled | - | - | - | +| user_filled | - | - | - | +| user_invisible | - | - | - | +| user_invisible_filled | - | - | - | +| user_list | - | - | - | +| user_list_filled | - | - | - | +| user_locked | - | - | - | +| user_locked_filled | - | - | - | +| user_marked | - | - | - | +| user_marked_filled | - | - | - | +| user_password | - | - | - | +| user_password_filled | - | - | - | +| user_safety | - | - | - | +| user_safety_filled | - | - | - | +| user_search | - | - | - | +| user_search_filled | - | - | - | +| user_setting | - | - | - | +| user_setting_filled | - | - | - | +| user_talk | - | - | - | +| user_talk_1 | - | - | - | +| user_talk_1_filled | - | - | - | +| user_talk_filled | - | - | - | +| user_talk_off_1 | - | - | - | +| user_talk_off_1_filled | - | - | - | +| user_time | - | - | - | +| user_time_filled | - | - | - | +| user_transmit | - | - | - | +| user_transmit_filled | - | - | - | +| user_unknown | - | - | - | +| user_unknown_filled | - | - | - | +| user_unlocked | - | - | - | +| user_unlocked_filled | - | - | - | +| user_vip | - | - | - | +| user_vip_filled | - | - | - | +| user_visible | - | - | - | +| user_visible_filled | - | - | - | +| usercase | - | - | - | +| usercase_filled | - | - | - | +| usercase_link | - | - | - | +| usercase_link_filled | - | - | - | +| usergroup | - | - | - | +| usergroup_add | - | - | - | +| usergroup_add_filled | - | - | - | +| usergroup_clear | - | - | - | +| usergroup_clear_filled | - | - | - | +| usergroup_filled | - | - | - | +| vehicle | - | - | - | +| vehicle_filled | - | - | - | +| verified | - | - | - | +| verified_filled | - | - | - | +| verify | - | - | - | +| verify_filled | - | - | - | +| vertical | - | - | - | +| vertical_filled | - | - | - | +| video | - | - | - | +| video_camera | - | - | - | +| video_camera_1 | - | - | - | +| video_camera_1_filled | - | - | - | +| video_camera_2 | - | - | - | +| video_camera_2_filled | - | - | - | +| video_camera_3 | - | - | - | +| video_camera_3_filled | - | - | - | +| video_camera_dollar | - | - | - | +| video_camera_dollar_filled | - | - | - | +| video_camera_filled | - | - | - | +| video_camera_minus | - | - | - | +| video_camera_minus_filled | - | - | - | +| video_camera_music | - | - | - | +| video_camera_music_filled | - | - | - | +| video_camera_off | - | - | - | +| video_camera_off_filled | - | - | - | +| video_filled | - | - | - | +| video_library | - | - | - | +| video_library_filled | - | - | - | +| view_agenda | - | - | - | +| view_agenda_filled | - | - | - | +| view_column | - | - | - | +| view_in_ar | - | - | - | +| view_in_ar_filled | - | - | - | +| view_list | - | - | - | +| view_module | - | - | - | +| view_module_filled | - | - | - | +| visual_recognition | - | - | - | +| visual_recognition_filled | - | - | - | +| wallet | - | - | - | +| wallet_filled | - | - | - | +| watch | - | - | - | +| watch_filled | - | - | - | +| watermelon | - | - | - | +| watermelon_filled | - | - | - | +| wave_bye | - | - | - | +| wave_bye_filled | - | - | - | +| wave_left | - | - | - | +| wave_left_filled | - | - | - | +| wave_right | - | - | - | +| wave_right_filled | - | - | - | +| wealth | - | - | - | +| wealth_1 | - | - | - | +| wealth_1_filled | - | - | - | +| wealth_filled | - | - | - | +| widget | - | - | - | +| widget_filled | - | - | - | +| wifi | - | - | - | +| wifi_1 | - | - | - | +| wifi_1_filled | - | - | - | +| wifi_off | - | - | - | +| wifi_off_1 | - | - | - | +| wifi_off_1_filled | - | - | - | +| window | - | - | - | +| window_1 | - | - | - | +| window_1_filled | - | - | - | +| window_filled | - | - | - | +| windy | - | - | - | +| windy_rain | - | - | - | +| wink | - | - | - | +| wink_filled | - | - | - | +| work | - | - | - | +| work_filled | - | - | - | +| work_history | - | - | - | +| work_history_filled | - | - | - | +| work_off | - | - | - | +| work_off_filled | - | - | - | +| wry_smile | - | - | - | +| wry_smile_filled | - | - | - | +| zoom_in | - | - | - | +| zoom_in_filled | - | - | - | +| zoom_out | - | - | - | +| zoom_out_filled | - | - | - | + #### 工厂构造方法 -| 名称 | 说明 | -| --- | --- | -| TIcons._ | 私有构造方法,不支持外部创建,仅提供静态常量给外部使用 | +##### TIcons._ + +私有构造方法,不支持外部创建,仅提供静态常量给外部使用 \ No newline at end of file diff --git a/tdesign-component/example/assets/api/image-viewer_api.md b/tdesign-component/example/assets/api/image-viewer_api.md index f36abefb8..1cc94ffd9 100644 --- a/tdesign-component/example/assets/api/image-viewer_api.md +++ b/tdesign-component/example/assets/api/image-viewer_api.md @@ -3,12 +3,42 @@ #### 静态方法 -| 名称 | 返回类型 | 参数 | 说明 | +##### TImageViewer.showImageViewer + +显示图片预览 + +返回类型:`void` + +| 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | -| showImageViewer | | required BuildContext context, required List images, List? labels, bool? closeBtn, bool? deleteBtn, bool? showIndex, bool? loop, bool? autoplay, int? duration, Color? bgColor, Color? navBarBgColor, Color? iconColor, TextStyle? labelStyle, TextStyle? indexStyle, Color? modalBarrierColor, bool? barrierDismissible, int? defaultIndex, double? width, double? height, OnIndexChange? onIndexChange, OnClose? onClose, OnDelete? onDelete, bool? ignoreDeleteError, OnImageTap? onTap, OnLongPress? onLongPress, LeftItemBuilder? leftItemBuilder, RightItemBuilder? rightItemBuilder, | 显示图片预览 | +| context | BuildContext | - | - | +| images | List | - | 图片数组 | +| labels | List? | - | 图片描述 | +| closeBtn | bool? | true | 是否展示关闭按钮 | +| deleteBtn | bool? | false | 是否显示删除操作 | +| showIndex | bool? | false | 是否显示页码 | +| loop | bool? | false | 图片是否循环 | +| autoplay | bool? | false | 图片轮播是否自动播放 | +| duration | int? | - | 自动播放间隔 | +| bgColor | Color? | - | 背景色 | +| navBarBgColor | Color? | - | 导航栏背景色 | +| iconColor | Color? | - | 图标颜色 | +| labelStyle | TextStyle? | - | label文字样式 | +| indexStyle | TextStyle? | - | 页码样式 | +| modalBarrierColor | Color? | - | - | +| barrierDismissible | bool? | - | - | +| defaultIndex | int? | - | 默认预览图片所在的下标 | +| width | double? | - | 图片宽度 | +| height | double? | - | 图片高度 | +| onIndexChange | OnIndexChange? | - | 预览图片切换回调 | +| onClose | OnClose? | - | 关闭点击 | +| onDelete | OnDelete? | - | 删除点击 | +| ignoreDeleteError | bool? | - | 是否忽略单张图片删除错误提示 | +| onTap | OnImageTap? | - | 点击图片 | +| onLongPress | OnLongPress? | - | 长按图片 | +| leftItemBuilder | LeftItemBuilder? | - | 左侧自定义操作 | +| rightItemBuilder | RightItemBuilder? | - | 右侧自定义操作 | -``` -``` ### TImageViewerWidget #### 默认构造方法 @@ -26,7 +56,7 @@ | ignoreDeleteError | bool? | false | 是否忽略单张图片删除错误提示 | | images | List | - | 图片数组 | | indexStyle | TextStyle? | - | 页码样式 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | labels | List? | - | 图片描述 | | labelStyle | TextStyle? | - | label文字样式 | | leftItemBuilder | LeftItemBuilder? | - | 左侧自定义操作 | @@ -40,3 +70,59 @@ | rightItemBuilder | RightItemBuilder? | - | 右侧自定义操作 | | showIndex | bool? | - | 是否显示页码 | | width | double? | - | 图片宽度 | + + +### OnIndexChange +#### 类型定义 + +```dart +typedef OnIndexChange = Function(int index); +``` + + +### OnClose +#### 类型定义 + +```dart +typedef OnClose = Function(int index); +``` + + +### OnDelete +#### 类型定义 + +```dart +typedef OnDelete = Function(int index); +``` + + +### OnImageTap +#### 类型定义 + +```dart +typedef OnImageTap = Function(int index); +``` + + +### OnLongPress +#### 类型定义 + +```dart +typedef OnLongPress = Function(int index); +``` + + +### LeftItemBuilder +#### 类型定义 + +```dart +typedef LeftItemBuilder = Widget Function(BuildContext context, int index); +``` + + +### RightItemBuilder +#### 类型定义 + +```dart +typedef RightItemBuilder = Widget Function(BuildContext context, int index); +``` diff --git a/tdesign-component/example/assets/api/image_api.md b/tdesign-component/example/assets/api/image_api.md index 133ef6adf..f49591872 100644 --- a/tdesign-component/example/assets/api/image_api.md +++ b/tdesign-component/example/assets/api/image_api.md @@ -4,30 +4,45 @@ | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | -| alignment | | Alignment.center | | +| alignment | AlignmentGeometry | Alignment.center | - | | assetUrl | String? | - | 本地素材地址 | -| cacheHeight | | - | | -| cacheWidth | | - | | -| centerSlice | | - | | -| color | | - | | -| colorBlendMode | | - | | -| errorBuilder | | - | | +| cacheHeight | int? | - | - | +| cacheWidth | int? | - | - | +| centerSlice | Rect? | - | - | +| color | Color? | - | - | +| colorBlendMode | BlendMode? | - | - | +| errorBuilder | ImageErrorWidgetBuilder? | - | - | | errorWidget | Widget? | - | 失败自定义提示 | -| excludeFromSemantics | | false | | -| filterQuality | | FilterQuality.low | | +| excludeFromSemantics | bool | false | - | +| filterQuality | FilterQuality | FilterQuality.low | - | | fit | BoxFit? | - | 适配样式 | | frameBuilder | ImageFrameBuilder? | - | 以下系统Image属性,释义请参考系统[Image]中注释 | -| gaplessPlayback | | false | | +| gaplessPlayback | bool | false | - | | height | double? | - | 自定义高 | | imageFile | File? | - | 图片文件路径 | | imgUrl | String? | - | 图片地址 | -| isAntiAlias | | false | | -| key | | - | | -| loadingBuilder | | - | | +| isAntiAlias | bool | false | - | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | +| loadingBuilder | ImageLoadingBuilder? | - | - | | loadingWidget | Widget? | - | 加载自定义提示 | -| matchTextDirection | | false | | -| opacity | | - | | -| repeat | | ImageRepeat.noRepeat | | -| semanticLabel | | - | | +| matchTextDirection | bool | false | - | +| opacity | Animation? | - | - | +| repeat | ImageRepeat | ImageRepeat.noRepeat | - | +| semanticLabel | String? | - | - | | type | TImageType | TImageType.roundedSquare | 图片类型 | | width | double? | - | 自定义宽 | + + +### TImageType +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| clip | 裁剪 | +| fitHeight | 适应高 | +| fitWidth | 适应宽 | +| stretch | 拉伸 | +| square | 方形, | +| roundedSquare | 圆角方形 | +| circle | 圆形 | diff --git a/tdesign-component/example/assets/api/indexes_api.md b/tdesign-component/example/assets/api/indexes_api.md index eeb9a7294..771f6eb82 100644 --- a/tdesign-component/example/assets/api/indexes_api.md +++ b/tdesign-component/example/assets/api/indexes_api.md @@ -12,7 +12,7 @@ | capsuleTheme | bool? | false | 锚点是否为胶囊式样式 | | indexList | List? | - | 索引字符列表。不传默认 A-Z | | indexListMaxHeight | double? | 0.8 | 索引列表最大高度(父容器高度的百分比,默认 0.8) | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | onChange | void Function(String index)? | - | 索引发生变更时触发事件 | | onSelect | void Function(String index)? | - | 点击侧边栏时触发事件 | | reverse | bool? | false | 反方向滚动置顶 | @@ -20,8 +20,6 @@ | sticky | bool? | true | 锚点是否吸顶 | | stickyOffset | double? | 0 | 锚点吸顶时与顶部的距离 | -``` -``` ### TIndexesAnchor #### 简介 @@ -33,12 +31,10 @@ | activeIndex | ValueNotifier | - | 选中索引 | | builderAnchor | Widget? Function(BuildContext context, String index, bool isPinnedToTop)? | - | 索引锚点构建 | | capsuleTheme | bool | - | 是否为胶囊式样式 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | sticky | bool | - | 索引是否吸顶 | | text | String | - | 锚点文本 | -``` -``` ### TIndexesList #### 简介 @@ -51,5 +47,5 @@ | builderIndex | Widget Function(BuildContext context, String index, bool isActive)? | - | 索引文本自定义构建,包括索引激活左侧提示 | | indexList | List | - | 索引字符列表。不传默认 A-Z | | indexListMaxHeight | double | 0.8 | 索引列表最大高度(父容器高度的百分比,默认0.8) | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | onSelect | void Function(String newIndex, String oldIndex) | - | 点击侧边栏时触发事件 | diff --git a/tdesign-component/example/assets/api/input_api.md b/tdesign-component/example/assets/api/input_api.md index 7d6f2ccf8..019509b1e 100644 --- a/tdesign-component/example/assets/api/input_api.md +++ b/tdesign-component/example/assets/api/input_api.md @@ -27,7 +27,7 @@ | inputDecoration | InputDecoration? | - | 自定义输入框样式,默认圆角 | | inputFormatters | List? | - | 显示输入内容,如限制长度(LengthLimitingTextInputFormatter(6)) | | inputType | TextInputType? | - | 键盘类型,数字、字母 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | labelWidget | Widget? | - | leftLabel右侧组件,支持自定义 | | leftContentSpace | double? | - | 输入框内容左侧间距 | | leftIcon | Widget? | - | 带图标的输入框 | @@ -52,9 +52,44 @@ | selectionControls | TextSelectionControls? | - | 自定义选择控制器 | | showBottomDivider | bool | true | 是否展示底部分割线 | | size | TInputSize | TInputSize.large | 输入框规格 | -| spacer | TInputSpacer | - | 组件各模块间间距 | +| spacer | TInputSpacer? | - | 组件各模块间间距 | | textAlign | TextAlign? | - | 文字对齐方向 | | textInputBackgroundColor | Color? | - | 文本框背景色 | | textStyle | TextStyle? | - | 文本颜色 | | type | TInputType | TInputType.normal | 输入框类型 | | width | double? | - | 输入框宽度(TCardStyle时必须设置该参数) | + + +### TInputType +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| normal | - | +| twoLine | - | +| longText | - | +| special | - | +| normalMaxTwoLine | - | +| cardStyle | - | + + +### TInputSize +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| small | - | +| large | - | + + +### TCardStyle +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| topText | - | +| topTextWithBlueBorder | - | +| errorStyle | - | diff --git a/tdesign-component/example/assets/api/link_api.md b/tdesign-component/example/assets/api/link_api.md index 7324abe45..2b2ac08fe 100644 --- a/tdesign-component/example/assets/api/link_api.md +++ b/tdesign-component/example/assets/api/link_api.md @@ -7,7 +7,7 @@ | color | Color? | - | link 文本的颜色,如果不设置则根据状态和风格进行计算 | | fontSize | double? | - | link 文本的字体大小,如果不设置则根据状态和风格进行计算 | | iconSize | double? | - | link icon 大小,如果不设置则根据状态和风格进行计算 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | label | String | - | link 展示的文本 | | leftGapWithIcon | double? | - | 前置icon和文本之间的间隔,如果不设置则根据状态和风格进行计算 | | linkClick | LinkClick? | - | link 被点击之后所采取的动作,会将uri当做参数传入到该方法当中 | @@ -19,3 +19,60 @@ | suffixIcon | Icon? | - | 后置 icon | | type | TLinkType | TLinkType.basic | link 类型 | | uri | Uri? | - | link 跳转的uri | + + +### TLinkType +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| basic | - | +| withUnderline | - | +| withPrefixIcon | - | +| withSuffixIcon | - | + + +### TLinkStyle +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| primary | - | +| defaultStyle | - | +| danger | - | +| warning | - | +| success | - | + + +### TLinkState +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| normal | - | +| active | - | +| disabled | - | + + +### TLinkSize +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| small | - | +| medium | - | +| large | - | + + +### LinkClick +#### 简介 +限制Function类型,防止传递错误的Function,导致参数对不上 +#### 类型定义 + +```dart +typedef LinkClick = Function(Uri? uri); +``` diff --git a/tdesign-component/example/assets/api/loading_api.md b/tdesign-component/example/assets/api/loading_api.md index 6bf7be714..f5fd3bf6f 100644 --- a/tdesign-component/example/assets/api/loading_api.md +++ b/tdesign-component/example/assets/api/loading_api.md @@ -9,8 +9,34 @@ | duration | int | 2000 | 一次刷新的时间,控制动画速度 | | icon | TLoadingIcon? | TLoadingIcon.circle | 图标,支持圆形、点状、菊花状 | | iconColor | Color? | - | 图标颜色 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | refreshWidget | Widget? | - | 失败刷新组件 | | size | TLoadingSize | - | 尺寸 | | text | String? | - | 文案 | | textColor | Color? | - | 文案颜色 | + + +### TLoadingSize +#### 简介 +Loading 尺寸 +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| small | 小尺寸 | +| medium | 中尺寸 | +| large | 大尺寸 | + + +### TLoadingIcon +#### 简介 +Loading图标 +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| circle | 圆形 | +| point | 点状 | +| activity | 菊花状 | diff --git a/tdesign-component/example/assets/api/message_api.md b/tdesign-component/example/assets/api/message_api.md index a327735d1..c1cfed62a 100644 --- a/tdesign-component/example/assets/api/message_api.md +++ b/tdesign-component/example/assets/api/message_api.md @@ -8,7 +8,7 @@ | content | String? | - | 通知内容 | | duration | int? | 3000 | 消息内置计时器 | | icon | dynamic | true | 自定义消息前面的图标 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | link | dynamic | - | 链接名称 | | marquee | MessageMarquee? | - | 跑马灯效果 | | offset | List? | - | 相对于 placement 的偏移量 | @@ -21,12 +21,26 @@ #### 静态方法 -| 名称 | 返回类型 | 参数 | 说明 | +##### TMessage.showMessage + +返回类型:`void` + +| 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | -| showMessage | | required BuildContext context, String? content, bool? visible, int? duration, dynamic closeBtn, dynamic icon, dynamic link, MessageMarquee? marquee, List? offset, MessageTheme? theme, VoidCallback? onCloseBtnClick, VoidCallback? onDurationEnd, VoidCallback? onLinkClick, | | +| context | BuildContext | - | - | +| content | String? | - | 通知内容 | +| visible | bool? | - | 是否显示 | +| duration | int? | - | 消息内置计时器 | +| closeBtn | dynamic | - | 关闭按钮 | +| icon | dynamic | - | 自定义消息前面的图标 | +| link | dynamic | - | 链接名称 | +| marquee | MessageMarquee? | - | 跑马灯效果 | +| offset | List? | - | 相对于 placement 的偏移量 | +| theme | MessageTheme? | - | 消息组件风格 info/success/warning/error | +| onCloseBtnClick | VoidCallback? | - | 点击关闭按钮触发 | +| onDurationEnd | VoidCallback? | - | 计时结束后触发 | +| onLinkClick | VoidCallback? | - | 点击链接文本时触发 | -``` -``` ### MessageMarquee #### 默认构造方法 @@ -37,8 +51,6 @@ | loop | int? | - | 循环次数 | | speed | int? | - | 速度 | -``` -``` ### MessageLink #### 默认构造方法 @@ -48,3 +60,17 @@ | color | Color? | - | 颜色 | | name | String | - | 名称 | | uri | Uri? | - | 资源链接 | + + +### MessageTheme +#### 简介 +定义消息主题枚举 +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| info | 普通通知 | +| success | 成功通知 | +| warning | 警示通知 | +| error | 错误通知 | diff --git a/tdesign-component/example/assets/api/navbar_api.md b/tdesign-component/example/assets/api/navbar_api.md index 49fab9f65..5bc4678a1 100644 --- a/tdesign-component/example/assets/api/navbar_api.md +++ b/tdesign-component/example/assets/api/navbar_api.md @@ -12,7 +12,7 @@ | centerTitle | bool | true | 标题是否居中 | | flexibleSpace | Widget? | - | 固定背景 | | height | double | 48 | 高度 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | leftBarItems | List? | - | 左边操作项 | | onBack | VoidCallback? | - | 返回事件 | | opacity | double | 1.0 | 透明度 | @@ -29,8 +29,6 @@ | useBorderStyle | bool | false | 是否使用边框模式 | | useDefaultBack | bool | true | 是否使用默认的返回 | -``` -``` ### TNavBarItem #### 默认构造方法 @@ -43,4 +41,12 @@ | iconColor | Color? | - | 图标颜色 | | iconSize | double? | 24.0 | 图标尺寸 | | iconWidget | Widget? | - | 图标组件,优先级高于 icon | -| padding | EdgeInsetsGeometry? | - | | +| padding | EdgeInsetsGeometry? | - | 内部填充 | + + +### TBarItemAction +#### 类型定义 + +```dart +typedef TBarItemAction = void Function(); +``` diff --git a/tdesign-component/example/assets/api/notice-bar_api.md b/tdesign-component/example/assets/api/notice-bar_api.md index f43768900..5f72f1e01 100644 --- a/tdesign-component/example/assets/api/notice-bar_api.md +++ b/tdesign-component/example/assets/api/notice-bar_api.md @@ -1,7 +1,5 @@ ## API ### TNoticeBar -#### 简介 - #### 默认构造方法 | 参数 | 类型 | 默认值 | 说明 | @@ -11,7 +9,7 @@ | direction | Axis? | Axis.horizontal | 滚动方向 | | height | double | 22 | 文字高度 (当使用prefixIcon或suffixIcon时,icon大小值等于该属性) | | interval | int? | 3000 | 步进滚动间隔时间(毫秒) | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | left | Widget? | - | 左侧内容(自定义左侧内容,优先级高于prefixIcon) | | marquee | bool? | false | 跑马灯效果 | | maxLines | int? | 1 | 文本行数(仅静态有效) | @@ -23,8 +21,6 @@ | suffixIcon | IconData? | - | 右侧图标 | | theme | TNoticeBarTheme? | TNoticeBarTheme.info | 主题 | -``` -``` ### TNoticeBarStyle #### 简介 @@ -43,6 +39,38 @@ #### 工厂构造方法 -| 名称 | 说明 | -| --- | --- | -| TNoticeBarStyle.generateTheme | 根据主题生成样式 | +##### TNoticeBarStyle.generateTheme + +根据主题生成样式 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| context | BuildContext | - | 上下文 | +| theme | TNoticeBarTheme? | TNoticeBarTheme.info | - | + + +### TNoticeBarType +#### 简介 +公告栏类型 +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| none | 静止(默认) | +| scroll | 滚动 | +| step | 步进 | + + +### TNoticeBarTheme +#### 简介 +公告栏主题 +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| info | 信息(默认) | +| success | 成功 | +| warning | 警告 | +| error | 错误 | diff --git a/tdesign-component/example/assets/api/picker_api.md b/tdesign-component/example/assets/api/picker_api.md index d09bcdf8f..57fbf9802 100644 --- a/tdesign-component/example/assets/api/picker_api.md +++ b/tdesign-component/example/assets/api/picker_api.md @@ -4,25 +4,23 @@ | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | -| cancel | Widget? | - | 工具栏左侧自定义插槽,默认使用 [TResourceDelegate.cancel] | -| confirm | Widget? | - | 工具栏右侧自定义插槽,默认使用 [TResourceDelegate.confirm] | +| cancel | Widget? | - | 工具栏左侧自定义插槽,默认使用 [TResourceDelegate.cancel] 可用于渲染图标、图标+文字组合等。点击事件依然由外层 [GestureDetector] 处理,触发 [onCancel] 回调——所以插槽内的 Widget 不需要自己处理点击。 ```dart // 简单改文字 TPicker( cancel: const Text('关闭'), onCancel: () => Navigator.of(context).pop(), ) // 带图标 TPicker( cancel: const Icon(Icons.close, size: 22), onCancel: () => Navigator.of(context).pop(), ) ``` | +| confirm | Widget? | - | 工具栏右侧自定义插槽,默认使用 [TResourceDelegate.confirm] 可用于渲染图标、图标+文字组合等。点击事件依然由外层 [GestureDetector] 处理,触发 [onConfirm] 回调——所以插槽内的 Widget 不需要自己处理点击。 ```dart // 简单改文字 TPicker( confirm: const Text('确定'), onConfirm: (v) => Navigator.of(context).pop(v), ) // 带图标 TPicker( confirm: const Icon(Icons.check, size: 22), onConfirm: (v) => Navigator.of(context).pop(v), ) ``` | | disabled | bool | false | 是否禁用整个选择器(禁止滚动和操作),默认 false | | height | double | 200 | 视窗高度,默认 200 | | initialValue | List? | - | 初始选中值列表(按 value 匹配) | | itemBuilder | ItemBuilderType? | - | 自定义子项构建器(disabled 项仍由内部统一渲染,不会走此 builder) | | itemCount | int | 5 | 每屏显示 item 数,默认 5 | | itemDistanceCalculator | ItemDistanceCalculator? | - | 自定义距离计算器(控制颜色/字重/字号随"离中心距离"的变化) | -| items | TPickerItems | - | 数据源(必填) | -| key | | - | | -| onCancel | VoidCallback? | - | 点击「取消」按钮回调 | -| onChange | void Function(TPickerValue)? | - | 值改变回调(滚动时实时触发) | -| onConfirm | void Function(TPickerValue)? | - | 点击「确认」按钮回调 | -| onLoad | void Function(TPickerLoadEvent)? | - | 列选中项变化的事件回调 | -| title | String? | - | 工具栏中部标题(可选,不传时中部留白) | -| titleWidget | Widget? | - | 工具栏中部自定义标题插槽 | - -``` -``` +| items | TPickerItems | - | 数据源(必填) 使用密封类 [TPickerItems] 编译期强制二选一: - [TPickerColumns] → 多列独立选择 - [TPickerLinked] → 联动选择 自由结构数据通过 `.fromRaw()` 工厂构造归一化。 | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | +| onCancel | VoidCallback? | - | 点击「取消」按钮回调 仅作为点击事件通知,不携带任何参数。组件本身不会做任何 popup 操作,业务层可在此自行决定是否关闭弹窗、重置状态等。 | +| onChange | void Function(TPickerValue)? | - | 值改变回调(滚动时实时触发) 触发时机: - 用户滚动经过某个 enabled 项并稳定时 - disabled 修正动画完成后,回调最终落点 **注意**:此回调代表"滚动时实时变化",不代表"用户已确认选择"。 如需"已确认"语义,请使用 [onConfirm]。 如需做网络请求/埋点等去抖处理,请在业务层自行 debounce。 | +| onConfirm | void Function(TPickerValue)? | - | 点击「确认」按钮回调 携带当前选中的完整 [TPickerValue],包含: - `selectedOptions`: 当前选中的所有 [TPickerOption] - `values`: 各列选中项的 value 列表 - `labels`: 各列选中项的 label 列表 - `indexes`: 各列选中项的索引 与 [onChange] 不同——只有用户点击「确认」时才触发,代表"已确认选择"。 组件本身不会做任何 popup 操作,业务层可在此自行决定是否关闭弹窗、 提交表单等。 | +| onLoad | void Function(TPickerLoadEvent)? | - | 列选中项变化的事件回调 **触发时机**:每次用户滚动到一个 enabled 项后都会触发(联动模式下还会 在新展开的列就位后触发)。组件本身不做"距底部多少项"的阈值判断——把 决策权交给业务层。 **事件参数**包含: - [TPickerLoadEvent.column]:触发列索引 - [TPickerLoadEvent.remaining]:当前列距底部剩余项数 - [TPickerLoadEvent.displayedCount]:当前列总项数 - [TPickerLoadEvent.parentValue]:联动模式下父级选中值(首列为 null) **典型用法**:业务层根据 [TPickerLoadEvent.remaining] 自行判断是否加载更多。 ```dart onLoad: (e) async { if (e.remaining > 5 \|\| _isLoading) return; // 距底部还远 / 已在加载,跳过 _isLoading = true; final more = await fetchMore(parent: e.parentValue); setState(() { _data.addAll(more); _isLoading = false; }); } ``` | +| title | String? | - | 工具栏中部标题(可选,不传时中部留白) 顶部工具栏永远显示,包含「取消」「标题」「确认」三块。 用户点击「取消」触发 [onCancel],点击「确认」触发 [onConfirm]。 选择器与弹窗(popup)完全解耦——关闭/打开弹窗的逻辑由业务层在 这两个回调中自行控制。 典型用法(与 popup 弹窗组合): ```dart TPicker( items: items, title: '请选择地区', onCancel: () => setState(() => visible = false), onConfirm: (value) { setState(() { selected = value; visible = false; }); }, ) ``` | +| titleWidget | Widget? | - | 工具栏中部自定义标题插槽 传入后会**完全替换**默认的 [title] 文字,可用于渲染更复杂的标题(副标题、图标+文字等)。 标题区域不响应点击。 | + ### TPickerOption #### 默认构造方法 @@ -33,8 +31,6 @@ | label | String | - | 展示文字(可包含 emoji、单位、国际化等) | | value | dynamic | - | 业务值(onChange 回调返回此字段) | -``` -``` ### TPickerValue #### 默认构造方法 @@ -44,8 +40,13 @@ | indexes | List | - | 每列选中项的索引 | | selectedOptions | List | - | 每列选中的完整 option | -``` -``` +#### 公开属性 + +| 属性 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| labels | List | - | 所有选中项的 label(展示用) 顺序与列顺序对应,可直接用于 UI 展示。 懒计算并缓存,生命周期内只计算一次。 | +| values | List | - | 所有选中项的 value(提交表单用) 顺序与列顺序对应,可直接用于表单提交。 懒计算并缓存,生命周期内只计算一次。 | + ### TPickerLoadEvent #### 默认构造方法 @@ -54,63 +55,71 @@ | --- | --- | --- | --- | | column | int | - | 触发事件的列索引(0 表示第一列) | | displayedCount | int | - | 当前列已展示的选项总数 | -| parentValue | dynamic | - | 当前列的父级选中值(联动模式下使用) | +| parentValue | dynamic | - | 当前列的父级选中值(联动模式下使用) 第一列时为 null;业务层可用此值从原始数据中筛选子级选项。 | | remaining | int | - | 距底部剩余的选项数(业务可用此值做"接近底部时加载"判断) | -``` -``` ### TPickerColumns #### 默认构造方法 | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | -| columns | List> | columns | 每列的选项列表 | +| columns | List> | - | 每列的选项列表 | #### 工厂构造方法 -| 名称 | 说明 | -| --- | --- | -| TPickerColumns.fromRaw | 从自由结构的 raw 数据创建,自动归一化 +##### TPickerColumns.fromRaw + +从自由结构的 raw 数据创建,自动归一化 ```dart TPickerColumns.fromRaw( [['北京', '上海', '广州']], keys: const TPickerKeys(label: 'name', value: 'code'), ) - ``` | + ``` + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| rawColumns | List | - | - | +| keys | TPickerKeys | TPickerKeys.defaults | - | -``` -``` ### TPickerLinked #### 默认构造方法 | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | -| tree | Map | tree | 联动树结构:`Map` | +| tree | Map | - | 联动树结构:`Map` value 可以是: - `Map` → 下一级联动 - `List` → 叶子级选项 | #### 工厂构造方法 -| 名称 | 说明 | -| --- | --- | -| TPickerLinked.fromRaw | 从自由结构的 raw Map 数据创建,自动归一化 +##### TPickerLinked.fromRaw + +从自由结构的 raw Map 数据创建,自动归一化 ```dart TPickerLinked.fromRaw({ '广东': {'深圳': ['南山', '福田'], '广州': ['天河']}, '浙江': {'杭州': ['西湖']}, }) - ``` | + ``` + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| rawTree | Map | - | - | +| keys | TPickerKeys | TPickerKeys.defaults | - | -``` -``` ### TPickerItems -``` -``` +#### 简介 +选择器数据源密封类 + + 编译期强制二选一,消除运行时类型错误: + - [TPickerColumns] → 多列独立选择 + - [TPickerLinked] → 联动选择 ### TPickerKeys #### 默认构造方法 @@ -121,3 +130,9 @@ | disabled | String | 'disabled' | 禁用标记对应的字段名,默认 `disabled` | | label | String | 'label' | 展示文案对应的字段名,默认 `label` | | value | String | 'value' | 业务值对应的字段名,默认 `value` | + +#### 静态成员 + +| 名称 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| defaults | TPickerKeys | - | 默认配置(`label / value / disabled / children`) | diff --git a/tdesign-component/example/assets/api/popover_api.md b/tdesign-component/example/assets/api/popover_api.md index 0a44f139d..bcf1ff4a9 100644 --- a/tdesign-component/example/assets/api/popover_api.md +++ b/tdesign-component/example/assets/api/popover_api.md @@ -1,20 +1,33 @@ ## API ### TPopover -#### 简介 - #### 静态方法 -| 名称 | 返回类型 | 参数 | 说明 | +##### TPopover.showPopover + +返回类型:`Future` + +| 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | -| showPopover | | required BuildContext context, String? content, Widget? contentWidget, double offset, TPopoverTheme? theme, bool closeOnClickOutside, TPopoverPlacement? placement, bool? showArrow, double arrowSize, EdgeInsetsGeometry? padding, double? width, double? height, Color? overlayColor, OnTap? onTap, OnLongTap? onLongTap, BorderRadius? radius, | | +| context | BuildContext | - | 上下文 | +| content | String? | - | 显示内容 | +| contentWidget | Widget? | - | 自定义内容 | +| offset | double | 4 | 偏移 | +| theme | TPopoverTheme? | - | 弹出气泡主题 | +| closeOnClickOutside | bool | true | - | +| placement | TPopoverPlacement? | - | 浮层出现位置 | +| showArrow | bool? | true | 是否显示浮层箭头 | +| arrowSize | double | 8 | 箭头大小 | +| padding | EdgeInsetsGeometry? | - | 内容内边距 | +| width | double? | - | 内容宽度(包含padding,实际高度:height - paddingLeft - paddingRight) | +| height | double? | - | 内容高度(包含padding,实际高度:height - paddingTop - paddingBottom) | +| overlayColor | Color? | Colors.transparent | - | +| onTap | OnTap? | - | 点击事件 | +| onLongTap | OnLongTap? | - | 长按事件 | +| radius | BorderRadius? | - | 圆角 | -``` -``` ### TPopoverWidget -#### 简介 - #### 默认构造方法 | 参数 | 类型 | 默认值 | 说明 | @@ -24,7 +37,7 @@ | contentWidget | Widget? | - | 自定义内容 | | context | BuildContext | - | 上下文 | | height | double? | - | 内容高度(包含padding,实际高度:height - paddingTop - paddingBottom) | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | offset | double | 4 | 偏移 | | onLongTap | OnLongTap? | - | 长按事件 | | onTap | OnTap? | - | 点击事件 | @@ -34,3 +47,53 @@ | showArrow | bool? | true | 是否显示浮层箭头 | | theme | TPopoverTheme? | - | 弹出气泡主题 | | width | double? | - | 内容宽度(包含padding,实际高度:height - paddingLeft - paddingRight) | + + +### TPopoverTheme +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| dark | 暗色 | +| light | 亮色 | +| info | 品牌色 | +| success | 成功 | +| warning | 警告 | +| error | 错误 | + + +### TPopoverPlacement +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| topLeft | 上左 | +| top | 上 | +| topRight | 上右 | +| rightTop | 右上 | +| right | 右 | +| rightBottom | 右下 | +| bottomRight | 下右 | +| bottom | 下 | +| bottomLeft | 下左 | +| leftBottom | 左下 | +| left | 左 | +| leftTop | 左上 | + + +### OnTap +#### 类型定义 + +```dart +typedef OnTap = Function(String? content); +``` + + +### OnLongTap +#### 类型定义 + +```dart +typedef OnLongTap = Function(String? content); +``` diff --git a/tdesign-component/example/assets/api/popup_api.md b/tdesign-component/example/assets/api/popup_api.md index 8ad1680cc..9ff884bdf 100644 --- a/tdesign-component/example/assets/api/popup_api.md +++ b/tdesign-component/example/assets/api/popup_api.md @@ -1,97 +1,343 @@ ## API -### TSlidePopupRoute +### TPopup #### 简介 -从屏幕的某个方向滑动弹出的Dialog框的路由,比如从顶部、底部、左、右滑出页面 -#### 默认构造方法 +弹出层入口:五向滑入 / 居中弹出,支持蒙层、bottom 操作区、center 面板外下方关闭区。 + + 通过 [show] 命令式打开;返回 [TPopupHandle] 用于关闭与再次打开。 + 多次调用 [show] 会继续压入新的浮层路由,可用于叠加展示。 + + **示例** + + ```dart + final handle = TPopup.show( + context, + options: TPopupOptions.bottom( + titleWidget: const Text('标题'), + child: MyPanel(), + ), + ); + handle.close(); + handle.open(); + ``` + + 配置项见 [TPopupOptions];方向见 [TPopupPlacement]。 + +#### 工厂构造方法 + +##### TPopup._ + +#### 静态方法 + +##### TPopup.show + +打开浮层并压入独立 [PopupRoute]。 + + [context] 用于查找 [Navigator] 并展示浮层。 + + [options] 浮层配置;方向固定时推荐 [TPopupOptions.bottom] 等命名工厂。 + + 返回 [TPopupHandle],可用 [TPopupHandle.close]、[TPopupHandle.open]、 + [TPopupHandle.isShowing] 控制与查询。 + 重复调用会继续 push 新的浮层;若需互斥请在业务层管理。 + + [navigatorContext] 可选,指定承载浮层的 [Navigator] 的 context,默认 [context]。 + + [useRootNavigator] 为 true 时使用根 [Navigator](嵌套导航场景)。 + +返回类型:`TPopupHandle` | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | -| barrierClick | VoidCallback? | - | 蒙层点击事件,仅在[modalBarrierFull]为false时触发 | -| barrierLabel | | - | | -| builder | WidgetBuilder | - | 控件构建器 | -| close | VoidCallback? | - | 关闭前事件 | -| focusMove | bool | false | 是否有输入框获取焦点时整体平移避免输入框被遮挡 | -| isDismissible | bool | true | 点击蒙层能否关闭 | -| modalBarrierColor | Color? | Colors.black54 | 蒙层颜色 | -| modalBarrierFull | bool | false | 是否全屏显示蒙层 | -| modalHeight | double? | - | 弹出框高度 | -| modalLeft | double? | 0 | 弹出框左侧距离 | -| modalTop | double? | 0 | 弹出框顶部距离 | -| modalWidth | double? | - | 弹出框宽度 | -| open | VoidCallback? | - | 打开前事件 | -| opened | VoidCallback? | - | 打开后事件 | -| slideTransitionFrom | SlideTransitionFrom | SlideTransitionFrom.bottom | 设置从屏幕的哪个方向滑出 | - -``` -``` - -### TPopupBottomDisplayPanel +| context | BuildContext | - | - | +| options | TPopupOptions | - | 创建时传入的配置;每次 [open] 会按 [TPopupOptions.placement] 裁剪无效字段后使用。 | +| navigatorContext | BuildContext? | - | 与 [TPopup.show] 的 [navigatorContext] 相同。 | +| useRootNavigator | bool | false | 与 [TPopup.show] 的 [useRootNavigator] 相同。 | + + +### TPopupOptions #### 简介 -右上角带关闭的底部浮层面板 +[TPopup.show] 的配置对象。 + + ## 如何创建 + + | 场景 | 推荐用法 | + |------|----------| + | 弹出方向已知 | [TPopupOptions.bottom]、[TPopupOptions.center]、[TPopupOptions.top]、[TPopupOptions.left]、[TPopupOptions.right] | + | 方向由变量决定 | 默认构造并设置 [placement];传错字段会在 [TPopup.show] / [TPopupHandle.open] 时抛 [FlutterError] | + + 命名工厂只暴露当前方向生效的字段(例如 [TPopupOptions.bottom] 无 [width] 参数)。 + + ## 字段与 [TPopupPlacement] + + | [TPopupPlacement] | 头部 / 关闭区 | 尺寸 | + |-------------------|-------------|------| + | [TPopupPlacement.bottom] | [headerBuilder]、[titleWidget]、[cancelBuilder]、[confirmBuilder] | [height]、[inset] | + | [TPopupPlacement.center] | [closeBuilder] | [width]、[height] | + | [TPopupPlacement.top] | — | [height]、[inset] | + | [TPopupPlacement.left]、[TPopupPlacement.right] | — | [width]、[inset] | + + ## Builder 三态([headerBuilder]、[cancelBuilder]、[confirmBuilder]、[closeBuilder]) + + | 传参方式 | 效果 | + |----------|------| + | 省略(使用默认值) | 渲染内置 UI | + | 显式 `null` | 隐藏该区域 | + | 自定义 [TPopupHeaderBuilder] / [TPopupSlotBuilder] | 完全替换;需自行提供交互与语义,可调用 `close` 关闭浮层 | + + [titleWidget] 默认为 `null`,表示无标题内容。 + + 生命周期回调见 [onOpen]、[onOpened]、[onClose]、[onClosed]、[onVisibleChange]、[onOverlayClick]。 #### 默认构造方法 | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | -| backgroundColor | | - | | -| child | | - | | -| closeClick | PopupClick? | - | 关闭按钮点击回调 | -| closeColor | Color? | - | 关闭按钮颜色 | -| closeSize | double? | - | 关闭按钮图标尺寸 | -| draggable | | - | | -| hideClose | bool | false | 是否隐藏关闭按钮 | -| key | | - | | -| maxHeightRatio | | - | | -| minHeightRatio | | - | | -| radius | | - | | -| title | | - | | -| titleColor | | - | | -| titleFontSize | double? | - | 标题字体大小 | -| titleLeft | bool | false | 标题是否靠左 | - -``` -``` - -### TPopupBottomConfirmPanel -#### 简介 -带确认的底部浮层面板 -#### 默认构造方法 +| animationDuration | Duration | const Duration(milliseconds: 240) | 打开/关闭动画时长。 | +| backgroundColor | Color? | - | 内容区背景色,默认主题容器色。 | +| cancelBuilder | TPopupSlotBuilder? | _kPopupDefaultCancel | bottom 左侧操作槽;仅 [headerBuilder] 为内置默认时生效。 内置默认为「取消」,点击触发 [TPopupTrigger.cancel]。 | +| child | Widget | - | 浮层主体内容(必填)。 | +| closeBuilder | TPopupSlotBuilder? | _kPopupDefaultClose | center 面板外下方关闭区;仅 [TPopupPlacement.center] 生效。三态见类文档「Builder 三态」。 内置默认点击触发 [TPopupTrigger.close]。 | +| closeOnOverlayClick | bool? | - | - | +| confirmBuilder | TPopupSlotBuilder? | _kPopupDefaultConfirm | bottom 右侧操作槽;仅 [headerBuilder] 为内置默认时生效。 内置默认为「确定」,点击触发 [TPopupTrigger.confirm]。 | +| destroyOnClose | bool | false | 为 true 时路由 `maintainState` 为 false,关闭后不保留路由内 State。 | +| headerBuilder | TPopupHeaderBuilder? | _kPopupDefaultHeader | bottom 头部;仅 [TPopupPlacement.bottom] 生效。三态见类文档「Builder 三态」。 自定义时忽略 [titleWidget]、[cancelBuilder]、[confirmBuilder]。 | +| height | double? | - | 高度;[TPopupPlacement.top]、[TPopupPlacement.bottom] 生效;[TPopupPlacement.center] 约束面板尺寸。 | +| inset | TPopupInset? | - | 交叉轴边缘留白;具体类型由 [placement] 决定。 * [TPopupPlacement.bottom] 使用 [TPopupBottomInset] * [TPopupPlacement.top] 使用 [TPopupTopInset] * [TPopupPlacement.left] 使用 [TPopupLeftInset] * [TPopupPlacement.right] 使用 [TPopupRightInset] * [TPopupPlacement.center] 不支持 | +| modal | bool | true | 是否以模态方式展示;为 true 时阻断背景交互与底层语义/焦点。 结合 [showOverlay] 可表达三种模式: * `modal=true, showOverlay=true`:标准模态弹层 * `modal=true, showOverlay=false`:透明模态弹层 * `modal=false, showOverlay=false`:非模态浮层 | +| onClose | VoidCallback? | - | 开始关闭(与 [onVisibleChange] 的 `visible: false` 同期)。 | +| onClosed | VoidCallback? | - | 当前展示周期真正结束。 大多数场景下会在关闭动画结束后触发;非栈顶路由被直接移除时不保证存在关闭动画。 | +| onOpen | VoidCallback? | - | 路由 push 时(打开动画开始前)。 | +| onOpened | VoidCallback? | - | 打开动画结束。 | +| onOverlayClick | VoidCallback? | - | 蒙层点击;是否关闭取决于 [closeOnOverlayClick]。 | +| onVisibleChange | TPopupVisibleChangeCallback? | - | 显隐变化;第二个参数为 [TPopupTrigger]。 | +| overlayColor | Color? | - | 蒙层颜色,默认 black54。 | +| overlayOpacity | double? | - | 蒙层透明度系数(0–1),与 [overlayColor] 的 alpha 相乘后用于绘制。 | +| placement | TPopupPlacement | TPopupPlacement.bottom | 出现位置,默认 [TPopupPlacement.bottom]。 | +| radius | double? | - | 内容区圆角,默认主题大圆角。 | +| showOverlay | bool | true | 是否绘制半透明蒙层。 当 [modal] 为 true 且此值为 false 时,为“透明模态弹层”。 | +| titleWidget | Widget? | - | bottom 标题插槽;仅 [headerBuilder] 为内置默认时生效。`null` 表示无标题。 | +| width | double? | - | 宽度;[TPopupPlacement.left]、[TPopupPlacement.right]、[TPopupPlacement.center] 生效。 | + + +#### 工厂构造方法 + +##### TPopupOptions.bottom + +创建 [TPopupPlacement.bottom] 配置。 + + 固定 [placement] 为 [TPopupPlacement.bottom];默认带内置头部。 + 蒙层、动画、生命周期等字段语义见同名成员文档。 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| child | Widget | - | 浮层主体内容(必填)。 | +| height | double? | - | 高度;[TPopupPlacement.top]、[TPopupPlacement.bottom] 生效;[TPopupPlacement.center] 约束面板尺寸。 | +| inset | TPopupBottomInset? | - | 交叉轴边缘留白;具体类型由 [placement] 决定。 * [TPopupPlacement.bottom] 使用 [TPopupBottomInset] * [TPopupPlacement.top] 使用 [TPopupTopInset] * [TPopupPlacement.left] 使用 [TPopupLeftInset] * [TPopupPlacement.right] 使用 [TPopupRightInset] * [TPopupPlacement.center] 不支持 | +| headerBuilder | TPopupHeaderBuilder? | _kPopupDefaultHeader | bottom 头部;仅 [TPopupPlacement.bottom] 生效。三态见类文档「Builder 三态」。 自定义时忽略 [titleWidget]、[cancelBuilder]、[confirmBuilder]。 | +| titleWidget | Widget? | - | bottom 标题插槽;仅 [headerBuilder] 为内置默认时生效。`null` 表示无标题。 | +| cancelBuilder | TPopupSlotBuilder? | _kPopupDefaultCancel | bottom 左侧操作槽;仅 [headerBuilder] 为内置默认时生效。 内置默认为「取消」,点击触发 [TPopupTrigger.cancel]。 | +| confirmBuilder | TPopupSlotBuilder? | _kPopupDefaultConfirm | bottom 右侧操作槽;仅 [headerBuilder] 为内置默认时生效。 内置默认为「确定」,点击触发 [TPopupTrigger.confirm]。 | +| radius | double? | - | 内容区圆角,默认主题大圆角。 | +| backgroundColor | Color? | - | 内容区背景色,默认主题容器色。 | +| showOverlay | bool | true | 是否绘制半透明蒙层。 当 [modal] 为 true 且此值为 false 时,为“透明模态弹层”。 | +| closeOnOverlayClick | bool? | - | - | +| overlayColor | Color? | - | 蒙层颜色,默认 black54。 | +| overlayOpacity | double? | - | 蒙层透明度系数(0–1),与 [overlayColor] 的 alpha 相乘后用于绘制。 | +| modal | bool | true | 是否以模态方式展示;为 true 时阻断背景交互与底层语义/焦点。 结合 [showOverlay] 可表达三种模式: * `modal=true, showOverlay=true`:标准模态弹层 * `modal=true, showOverlay=false`:透明模态弹层 * `modal=false, showOverlay=false`:非模态浮层 | +| destroyOnClose | bool | false | 为 true 时路由 `maintainState` 为 false,关闭后不保留路由内 State。 | +| animationDuration | Duration | const Duration(milliseconds: 240) | 打开/关闭动画时长。 | +| onOpen | VoidCallback? | - | 路由 push 时(打开动画开始前)。 | +| onOpened | VoidCallback? | - | 打开动画结束。 | +| onClose | VoidCallback? | - | 开始关闭(与 [onVisibleChange] 的 `visible: false` 同期)。 | +| onClosed | VoidCallback? | - | 当前展示周期真正结束。 大多数场景下会在关闭动画结束后触发;非栈顶路由被直接移除时不保证存在关闭动画。 | +| onVisibleChange | TPopupVisibleChangeCallback? | - | 显隐变化;第二个参数为 [TPopupTrigger]。 | +| onOverlayClick | VoidCallback? | - | 蒙层点击;是否关闭取决于 [closeOnOverlayClick]。 | + + +##### TPopupOptions.center + +创建 [TPopupPlacement.center] 配置。 + + 固定 [placement] 为 [TPopupPlacement.center];默认展示面板外下方圆形关闭按钮。 | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | -| backgroundColor | | - | | -| child | | - | | -| draggable | | - | | -| key | | - | | -| leftClick | PopupClick? | - | 左边文本点击回调 | -| leftText | String? | - | 左边文本 | -| leftTextColor | Color? | - | 左边文本颜色 | -| leftTextFontSize | double? | - | 左边文本字体大小 | -| maxHeightRatio | | - | | -| minHeightRatio | | - | | -| radius | | - | | -| rightClick | PopupClick? | - | 右边文本点击回调 | -| rightText | String? | - | 右边文本 | -| rightTextColor | Color? | - | 右边文本颜色 | -| rightTextFontSize | double? | - | 右边文本字体大小 | -| title | | - | | -| titleColor | | - | | -| titleFontSize | double? | - | 标题字体大小 | - -``` -``` - -### TPopupCenterPanel +| child | Widget | - | 浮层主体内容(必填)。 | +| width | double? | - | 宽度;[TPopupPlacement.left]、[TPopupPlacement.right]、[TPopupPlacement.center] 生效。 | +| height | double? | - | 高度;[TPopupPlacement.top]、[TPopupPlacement.bottom] 生效;[TPopupPlacement.center] 约束面板尺寸。 | +| closeBuilder | TPopupSlotBuilder? | _kPopupDefaultClose | center 面板外下方关闭区;仅 [TPopupPlacement.center] 生效。三态见类文档「Builder 三态」。 内置默认点击触发 [TPopupTrigger.close]。 | +| radius | double? | - | 内容区圆角,默认主题大圆角。 | +| backgroundColor | Color? | - | 内容区背景色,默认主题容器色。 | +| showOverlay | bool | true | 是否绘制半透明蒙层。 当 [modal] 为 true 且此值为 false 时,为“透明模态弹层”。 | +| closeOnOverlayClick | bool? | - | - | +| overlayColor | Color? | - | 蒙层颜色,默认 black54。 | +| overlayOpacity | double? | - | 蒙层透明度系数(0–1),与 [overlayColor] 的 alpha 相乘后用于绘制。 | +| modal | bool | true | 是否以模态方式展示;为 true 时阻断背景交互与底层语义/焦点。 结合 [showOverlay] 可表达三种模式: * `modal=true, showOverlay=true`:标准模态弹层 * `modal=true, showOverlay=false`:透明模态弹层 * `modal=false, showOverlay=false`:非模态浮层 | +| destroyOnClose | bool | false | 为 true 时路由 `maintainState` 为 false,关闭后不保留路由内 State。 | +| animationDuration | Duration | const Duration(milliseconds: 240) | 打开/关闭动画时长。 | +| onOpen | VoidCallback? | - | 路由 push 时(打开动画开始前)。 | +| onOpened | VoidCallback? | - | 打开动画结束。 | +| onClose | VoidCallback? | - | 开始关闭(与 [onVisibleChange] 的 `visible: false` 同期)。 | +| onClosed | VoidCallback? | - | 当前展示周期真正结束。 大多数场景下会在关闭动画结束后触发;非栈顶路由被直接移除时不保证存在关闭动画。 | +| onVisibleChange | TPopupVisibleChangeCallback? | - | 显隐变化;第二个参数为 [TPopupTrigger]。 | +| onOverlayClick | VoidCallback? | - | 蒙层点击;是否关闭取决于 [closeOnOverlayClick]。 | + + +##### TPopupOptions.left + +创建 [TPopupPlacement.left] 配置。 + + 固定 [placement] 为 [TPopupPlacement.left];未传 [width] 时布局默认宽度 280。 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| child | Widget | - | 浮层主体内容(必填)。 | +| width | double? | - | 宽度;[TPopupPlacement.left]、[TPopupPlacement.right]、[TPopupPlacement.center] 生效。 | +| inset | TPopupLeftInset? | - | 交叉轴边缘留白;具体类型由 [placement] 决定。 * [TPopupPlacement.bottom] 使用 [TPopupBottomInset] * [TPopupPlacement.top] 使用 [TPopupTopInset] * [TPopupPlacement.left] 使用 [TPopupLeftInset] * [TPopupPlacement.right] 使用 [TPopupRightInset] * [TPopupPlacement.center] 不支持 | +| radius | double? | - | 内容区圆角,默认主题大圆角。 | +| backgroundColor | Color? | - | 内容区背景色,默认主题容器色。 | +| showOverlay | bool | true | 是否绘制半透明蒙层。 当 [modal] 为 true 且此值为 false 时,为“透明模态弹层”。 | +| closeOnOverlayClick | bool? | - | - | +| overlayColor | Color? | - | 蒙层颜色,默认 black54。 | +| overlayOpacity | double? | - | 蒙层透明度系数(0–1),与 [overlayColor] 的 alpha 相乘后用于绘制。 | +| modal | bool | true | 是否以模态方式展示;为 true 时阻断背景交互与底层语义/焦点。 结合 [showOverlay] 可表达三种模式: * `modal=true, showOverlay=true`:标准模态弹层 * `modal=true, showOverlay=false`:透明模态弹层 * `modal=false, showOverlay=false`:非模态浮层 | +| destroyOnClose | bool | false | 为 true 时路由 `maintainState` 为 false,关闭后不保留路由内 State。 | +| animationDuration | Duration | const Duration(milliseconds: 240) | 打开/关闭动画时长。 | +| onOpen | VoidCallback? | - | 路由 push 时(打开动画开始前)。 | +| onOpened | VoidCallback? | - | 打开动画结束。 | +| onClose | VoidCallback? | - | 开始关闭(与 [onVisibleChange] 的 `visible: false` 同期)。 | +| onClosed | VoidCallback? | - | 当前展示周期真正结束。 大多数场景下会在关闭动画结束后触发;非栈顶路由被直接移除时不保证存在关闭动画。 | +| onVisibleChange | TPopupVisibleChangeCallback? | - | 显隐变化;第二个参数为 [TPopupTrigger]。 | +| onOverlayClick | VoidCallback? | - | 蒙层点击;是否关闭取决于 [closeOnOverlayClick]。 | + + +##### TPopupOptions.right + +创建 [TPopupPlacement.right] 配置。 + + 固定 [placement] 为 [TPopupPlacement.right];未传 [width] 时布局默认宽度 280。 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| child | Widget | - | 浮层主体内容(必填)。 | +| width | double? | - | 宽度;[TPopupPlacement.left]、[TPopupPlacement.right]、[TPopupPlacement.center] 生效。 | +| inset | TPopupRightInset? | - | 交叉轴边缘留白;具体类型由 [placement] 决定。 * [TPopupPlacement.bottom] 使用 [TPopupBottomInset] * [TPopupPlacement.top] 使用 [TPopupTopInset] * [TPopupPlacement.left] 使用 [TPopupLeftInset] * [TPopupPlacement.right] 使用 [TPopupRightInset] * [TPopupPlacement.center] 不支持 | +| radius | double? | - | 内容区圆角,默认主题大圆角。 | +| backgroundColor | Color? | - | 内容区背景色,默认主题容器色。 | +| showOverlay | bool | true | 是否绘制半透明蒙层。 当 [modal] 为 true 且此值为 false 时,为“透明模态弹层”。 | +| closeOnOverlayClick | bool? | - | - | +| overlayColor | Color? | - | 蒙层颜色,默认 black54。 | +| overlayOpacity | double? | - | 蒙层透明度系数(0–1),与 [overlayColor] 的 alpha 相乘后用于绘制。 | +| modal | bool | true | 是否以模态方式展示;为 true 时阻断背景交互与底层语义/焦点。 结合 [showOverlay] 可表达三种模式: * `modal=true, showOverlay=true`:标准模态弹层 * `modal=true, showOverlay=false`:透明模态弹层 * `modal=false, showOverlay=false`:非模态浮层 | +| destroyOnClose | bool | false | 为 true 时路由 `maintainState` 为 false,关闭后不保留路由内 State。 | +| animationDuration | Duration | const Duration(milliseconds: 240) | 打开/关闭动画时长。 | +| onOpen | VoidCallback? | - | 路由 push 时(打开动画开始前)。 | +| onOpened | VoidCallback? | - | 打开动画结束。 | +| onClose | VoidCallback? | - | 开始关闭(与 [onVisibleChange] 的 `visible: false` 同期)。 | +| onClosed | VoidCallback? | - | 当前展示周期真正结束。 大多数场景下会在关闭动画结束后触发;非栈顶路由被直接移除时不保证存在关闭动画。 | +| onVisibleChange | TPopupVisibleChangeCallback? | - | 显隐变化;第二个参数为 [TPopupTrigger]。 | +| onOverlayClick | VoidCallback? | - | 蒙层点击;是否关闭取决于 [closeOnOverlayClick]。 | + + +##### TPopupOptions.top + +创建 [TPopupPlacement.top] 配置。 + + 固定 [placement] 为 [TPopupPlacement.top];无内置头部。 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| child | Widget | - | 浮层主体内容(必填)。 | +| height | double? | - | 高度;[TPopupPlacement.top]、[TPopupPlacement.bottom] 生效;[TPopupPlacement.center] 约束面板尺寸。 | +| inset | TPopupTopInset? | - | 交叉轴边缘留白;具体类型由 [placement] 决定。 * [TPopupPlacement.bottom] 使用 [TPopupBottomInset] * [TPopupPlacement.top] 使用 [TPopupTopInset] * [TPopupPlacement.left] 使用 [TPopupLeftInset] * [TPopupPlacement.right] 使用 [TPopupRightInset] * [TPopupPlacement.center] 不支持 | +| radius | double? | - | 内容区圆角,默认主题大圆角。 | +| backgroundColor | Color? | - | 内容区背景色,默认主题容器色。 | +| showOverlay | bool | true | 是否绘制半透明蒙层。 当 [modal] 为 true 且此值为 false 时,为“透明模态弹层”。 | +| closeOnOverlayClick | bool? | - | - | +| overlayColor | Color? | - | 蒙层颜色,默认 black54。 | +| overlayOpacity | double? | - | 蒙层透明度系数(0–1),与 [overlayColor] 的 alpha 相乘后用于绘制。 | +| modal | bool | true | 是否以模态方式展示;为 true 时阻断背景交互与底层语义/焦点。 结合 [showOverlay] 可表达三种模式: * `modal=true, showOverlay=true`:标准模态弹层 * `modal=true, showOverlay=false`:透明模态弹层 * `modal=false, showOverlay=false`:非模态浮层 | +| destroyOnClose | bool | false | 为 true 时路由 `maintainState` 为 false,关闭后不保留路由内 State。 | +| animationDuration | Duration | const Duration(milliseconds: 240) | 打开/关闭动画时长。 | +| onOpen | VoidCallback? | - | 路由 push 时(打开动画开始前)。 | +| onOpened | VoidCallback? | - | 打开动画结束。 | +| onClose | VoidCallback? | - | 开始关闭(与 [onVisibleChange] 的 `visible: false` 同期)。 | +| onClosed | VoidCallback? | - | 当前展示周期真正结束。 大多数场景下会在关闭动画结束后触发;非栈顶路由被直接移除时不保证存在关闭动画。 | +| onVisibleChange | TPopupVisibleChangeCallback? | - | 显隐变化;第二个参数为 [TPopupTrigger]。 | +| onOverlayClick | VoidCallback? | - | 蒙层点击;是否关闭取决于 [closeOnOverlayClick]。 | + + +### TPopupHandle #### 简介 -居中浮层面板 -#### 默认构造方法 +[TPopup.show] 的返回值,用于控制同一份 [TPopupOptions] 的多次打开与关闭。 + + **示例** + + ```dart + final handle = TPopup.show( + context, + options: TPopupOptions.bottom(child: panel), + ); + handle.close(); + handle.open(); // 可省略 context,复用已缓存的 Navigator + ``` +#### 公开属性 + +| 属性 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| navigatorContext | BuildContext? | - | 与 [TPopup.show] 的 [navigatorContext] 相同。 | +| options | TPopupOptions | - | 创建时传入的配置;每次 [open] 会按 [TPopupOptions.placement] 裁剪无效字段后使用。 | +| useRootNavigator | bool | - | 与 [TPopup.show] 的 [useRootNavigator] 相同。 | + + +#### 工厂构造方法 + +##### TPopupHandle._ | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | -| backgroundColor | Color? | - | 背景颜色 | -| child | Widget | - | 子控件 | -| closeClick | PopupClick? | - | 关闭按钮点击回调 | -| closeColor | Color? | - | 关闭按钮颜色 | -| closeSize | double? | - | 关闭按钮图标尺寸 | -| closeUnderBottom | bool | false | 关闭按钮是否在视图框下方 | -| key | | - | | -| radius | double? | - | 圆角 | +| options | TPopupOptions | - | 创建时传入的配置;每次 [open] 会按 [TPopupOptions.placement] 裁剪无效字段后使用。 | +| navigatorContext | BuildContext? | - | 与 [TPopup.show] 的 [navigatorContext] 相同。 | +| useRootNavigator | bool | false | 与 [TPopup.show] 的 [useRootNavigator] 相同。 | + + +### TPopupPlacement +#### 简介 +浮层出现方向;决定 [TPopupOptions] 中哪些字段生效。 + + 与 [TPopupOptions] 类文档中的「字段与 placement」表对应。 + 方向固定时请用 [TPopupOptions.bottom]、[TPopupOptions.center] 等命名工厂。 +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| top | 自顶部滑入;使用 [TPopupOptions.height]、[TPopupOptions.inset]([TPopupTopInset])。 | +| left | 自左侧滑入;使用 [TPopupOptions.width]、[TPopupOptions.inset]([TPopupLeftInset])。 | +| right | 自右侧滑入;使用 [TPopupOptions.width]、[TPopupOptions.inset]([TPopupRightInset])。 | +| bottom | 自底部滑入;默认内置头部;使用 [TPopupOptions.height]、[TPopupOptions.inset]([TPopupBottomInset])。 | +| center | 屏幕居中;使用 [TPopupOptions.closeBuilder] 控制面板外下方关闭区。 | + + +### TPopupTrigger +#### 简介 +浮层关闭或显隐变化时的触发来源。 + + 作为 [TPopupVisibleChangeCallback] 的第二个参数,以及关闭流程中的语义标记。 + + 内置控件会映射为 [TPopupTrigger.overlay]、[TPopupTrigger.cancel]、 + [TPopupTrigger.confirm]、[TPopupTrigger.close]; + [TPopupHandle.close] 为 [TPopupTrigger.api];系统返回为 + [TPopupTrigger.systemBack];[headerBuilder] 内调用 `close` 等为 + [TPopupTrigger.custom]。 +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| overlay | 点击蒙层,且 [TPopupOptions.closeOnOverlayClick] 为 true。 | +| cancel | 点击 bottom 取消语义槽位(含默认与自定义 [TPopupOptions.cancelBuilder])。 | +| confirm | 点击 bottom 确认语义槽位(含默认与自定义 [TPopupOptions.confirmBuilder])。 | +| close | 点击 center 关闭语义槽位(含默认与自定义 [TPopupOptions.closeBuilder])。 | +| api | 外部 API 主动触发的显隐变化,如 [TPopupHandle.close] 或打开事件。 | +| systemBack | 系统返回键或系统路由返回触发的关闭。 | +| custom | 无框架预设动作语义的自定义关闭,如 [headerBuilder] 内调用 `close`。 | diff --git a/tdesign-component/example/assets/api/progress_api.md b/tdesign-component/example/assets/api/progress_api.md index 44b2617be..090884e04 100644 --- a/tdesign-component/example/assets/api/progress_api.md +++ b/tdesign-component/example/assets/api/progress_api.md @@ -4,12 +4,12 @@ | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | -| animationDuration | int? | 300 | 动画持续时间(正整数,单位为毫秒) | +| animationDuration | int | 300 | 动画持续时间(正整数,单位为毫秒) | | backgroundColor | Color? | - | 进度条背景颜色 | | circleRadius | double? | - | 环形进度条半径(正数) | | color | Color? | - | 进度条颜色 | | customProgressLabel | Widget? | - | 自定义标签 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | label | TLabelWidget? | - | 进度条标签 | | labelWidgetAlignment | Alignment? | - | 自定义标签对齐方式 | | labelWidgetWidth | double? | - | 自定义标签宽度 | @@ -22,3 +22,38 @@ | strokeWidth | double? | - | 进度条粗细(正数) | | type | TProgressType | - | 进度条类型 | | value | double? | - | 进度值(0.0 到 1.0 之间的正数) | + + +### TProgressType +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| linear | - | +| circular | - | +| micro | - | +| button | - | + + +### TProgressLabelPosition +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| inside | - | +| left | - | +| right | - | + + +### TProgressStatus +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| primary | - | +| warning | - | +| danger | - | +| success | - | diff --git a/tdesign-component/example/assets/api/pull-down-refresh_api.md b/tdesign-component/example/assets/api/pull-down-refresh_api.md index 3ade18761..c819e7919 100644 --- a/tdesign-component/example/assets/api/pull-down-refresh_api.md +++ b/tdesign-component/example/assets/api/pull-down-refresh_api.md @@ -8,38 +8,38 @@ TDesign刷新头部 | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | backgroundColor | Color? | - | 背景颜色 | -| clamping | | - | | +| clamping | bool? | - | - | | completeDuration | Duration? | - | 完成延时 | | enableHapticFeedback | bool | true | 开启震动反馈 | | enableInfiniteRefresh | bool | false | 是否开启无限刷新 | | extent | double? | 48.0 | Header容器高度 | | float | bool | false | 是否悬浮 | -| frictionFactor | | - | | -| hapticFeedback | | - | | -| hitOver | | - | | -| horizontalFrictionFactor | | - | | -| horizontalReadySpringBuilder | | - | | -| horizontalSpring | | - | | -| infiniteHitOver | | - | | +| frictionFactor | - | - | - | +| hapticFeedback | bool? | - | - | +| hitOver | - | - | - | +| horizontalFrictionFactor | - | - | - | +| horizontalReadySpringBuilder | - | - | - | +| horizontalSpring | - | - | - | +| infiniteHitOver | bool? | - | - | | infiniteOffset | double? | - | 无限刷新偏移量 | | key | Key? | - | Key | -| listenable | | - | | +| listenable | - | - | - | | loadingIcon | TLoadingIcon | TLoadingIcon.circle | loading样式 | -| maxOverOffset | | - | | -| notifyWhenInvisible | | - | | +| maxOverOffset | - | - | - | +| notifyWhenInvisible | - | - | - | | overScroll | bool | true | 越界滚动([enableInfiniteRefresh]为true或[infiniteOffset]有值时生效) | -| position | | - | | -| processedDuration | | - | | -| readySpringBuilder | | - | | -| safeArea | | false | | -| secondaryCloseTriggerOffset | | - | | -| secondaryDimension | | - | | -| secondaryTriggerOffset | | - | | -| secondaryVelocity | | - | | -| spring | | - | | -| springRebound | | - | | +| position | - | - | - | +| processedDuration | Duration? | - | - | +| readySpringBuilder | - | - | - | +| safeArea | - | false | - | +| secondaryCloseTriggerOffset | - | - | - | +| secondaryDimension | - | - | - | +| secondaryTriggerOffset | - | - | - | +| secondaryVelocity | - | - | - | +| spring | - | - | - | +| springRebound | - | - | - | | triggerDistance | double | 48.0 | 触发刷新任务的偏移量,同[triggerOffset] | -| triggerOffset | | - | | -| triggerWhenReach | | - | | -| triggerWhenRelease | | - | | -| triggerWhenReleaseNoWait | | - | | +| triggerOffset | double? | - | - | +| triggerWhenReach | - | - | - | +| triggerWhenRelease | - | - | - | +| triggerWhenReleaseNoWait | - | - | - | diff --git a/tdesign-component/example/assets/api/radio_api.md b/tdesign-component/example/assets/api/radio_api.md index 74529e483..3ae5d569b 100644 --- a/tdesign-component/example/assets/api/radio_api.md +++ b/tdesign-component/example/assets/api/radio_api.md @@ -6,34 +6,32 @@ | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | -| backgroundColor | | - | | -| cardMode | | - | | -| checkBoxLeftSpace | | - | | -| contentDirection | | TContentDirection.right | | -| customContentBuilder | | - | | -| customIconBuilder | | - | | -| customSpace | | - | | -| disableColor | | - | | -| enable | | true | | -| id | | - | | -| insetSpacing | | - | | -| key | | - | | +| backgroundColor | Color? | - | - | +| cardMode | bool? | - | - | +| checkBoxLeftSpace | double? | - | - | +| contentDirection | TContentDirection | TContentDirection.right | - | +| customContentBuilder | ContentBuilder? | - | - | +| customIconBuilder | IconBuilder? | - | - | +| customSpace | EdgeInsetsGeometry? | - | - | +| disableColor | Color? | - | - | +| enable | bool | true | - | +| id | String? | - | - | +| insetSpacing | double? | - | - | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | radioStyle | TRadioStyle | TRadioStyle.circle | 单选框按钮样式 | -| selectColor | | - | | -| showDivider | bool | - | 是否显示下划线 | -| size | | TCheckBoxSize.small | | -| spacing | | - | | -| subTitle | | - | | -| subTitleColor | | - | | -| subTitleFont | | - | | -| subTitleMaxLine | | 1 | | -| title | | - | | -| titleColor | | - | | -| titleFont | | - | | -| titleMaxLine | | 1 | | +| selectColor | Color? | - | - | +| showDivider | bool? | - | - | +| size | TCheckBoxSize | TCheckBoxSize.small | - | +| spacing | double? | - | - | +| subTitle | String? | - | - | +| subTitleColor | Color? | - | - | +| subTitleFont | Font? | - | - | +| subTitleMaxLine | int | 1 | - | +| title | String? | - | - | +| titleColor | Color? | - | - | +| titleFont | Font? | - | - | +| titleMaxLine | int | 1 | - | -``` -``` ### TRadioGroup #### 简介 @@ -46,22 +44,42 @@ RadioGroup分组对象,继承自TCheckboxGroup,字段含义与父类一致 | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | -| cardMode | | false | | -| child | | - | | -| contentDirection | | - | | -| controller | | - | | -| customContentBuilder | | - | | -| customIconBuilder | | - | | -| direction | | - | | -| directionalTdRadios | | - | | +| cardMode | bool | false | - | +| child | Widget? | - | - | +| contentDirection | TContentDirection? | - | - | +| controller | TCheckboxGroupController? | - | - | +| customContentBuilder | ContentBuilder? | - | - | +| customIconBuilder | IconBuilder? | - | - | +| direction | Axis? | - | - | +| directionalTdRadios | List? | - | - | | divider | Widget? | - | 自定义下划线 | -| key | | - | | -| onRadioGroupChange | | - | | -| passThrough | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | +| onRadioGroupChange | OnRadioGroupChange? | - | - | +| passThrough | bool? | - | - | | radioCheckStyle | TRadioStyle? | - | 勾选样式 | | rowCount | int | 1 | 每行几列 | -| selectId | | - | | +| selectId | String? | - | - | | showDivider | bool | false | 是否显示下划线 | -| spacing | | - | | +| spacing | double? | - | - | | strictMode | bool | true | 严格模式下,用户不能取消勾选,只能切换选择项, | -| titleMaxLine | | - | | +| titleMaxLine | int? | - | - | + + +### TRadioStyle +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| circle | - | +| square | - | +| check | - | +| hollowCircle | - | + + +### OnRadioGroupChange +#### 类型定义 + +```dart +typedef OnRadioGroupChange = void Function(String? selectedId); +``` diff --git a/tdesign-component/example/assets/api/rate_api.md b/tdesign-component/example/assets/api/rate_api.md index d3089c22e..6fd415996 100644 --- a/tdesign-component/example/assets/api/rate_api.md +++ b/tdesign-component/example/assets/api/rate_api.md @@ -5,7 +5,7 @@ | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | allowHalf | bool? | false | 是否允许半选 | -| builderText | Widget Function(BuildContext context, double value)? | - | 评分等级对应的辅助文字自定义构建,优先级高于[texts] | +| builderText | Widget Function(BuildContext context, double value)? | - | 评分等级对应的辅助文字自定义构建,优先级高于[texts] 配置后,会忽略[texts],[textWidth],[iconTextGap] | | color | List? | - | 评分图标的颜色,示例:[选中颜色] / [选中颜色,未选中颜色],默认:[TTheme.of(context).warningColor5, TTheme.of(context).grayColor4] | | count | int? | 5 | 评分的数量 | | crossAxisAlignment | CrossAxisAlignment? | CrossAxisAlignment.center | 评分图标与辅助文字的交叉轴对齐方式 | @@ -14,13 +14,24 @@ | gap | double? | - | 评分图标的间距,默认:TTheme.of(context).spacer8 | | icon | List? | - | 自定义评分图标,[选中和未选中图标] / [选中图标,未选中图标],默认:[TIcons.star_filled] | | iconTextGap | double? | - | 评分图标与辅助文字的间距,默认:[TTheme.of(context).spacer16] | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | mainAxisAlignment | MainAxisAlignment? | MainAxisAlignment.start | 评分图标与辅助文字的主轴对齐方式 | | mainAxisSize | MainAxisSize? | MainAxisSize.min | 评分图标与辅助文字主轴方向上如何占用空间 | | onChange | void Function(double value)? | - | 评分数改变时触发 | | placement | PlacementEnum? | PlacementEnum.top | 选择评分弹框的位置,值为[PlacementEnum.none]表示不显示评分弹框。 | | showText | bool? | false | 是否显示对应的辅助文字 | | size | double? | 24.0 | 评分图标的大小 | -| texts | List? | const ['极差', '失望', '一般', '满意', '惊喜'] | 评分等级对应的辅助文字, | +| texts | List? | const ['极差', '失望', '一般', '满意', '惊喜'] | 评分等级对应的辅助文字, 当[allowHalf]为false时长度应与[count]一致, 当[allowHalf]为true时长度应为[count]的两倍, 自定义值示例:['1分', '2分', '3分', '4分', '5分']。 | | textWidth | double? | 48.0 | 评分等级对应的辅助文字宽度 | | value | double? | 0 | 选择评分的值 | + + +### PlacementEnum +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| none | - | +| top | - | +| bottom | - | diff --git a/tdesign-component/example/assets/api/result_api.md b/tdesign-component/example/assets/api/result_api.md index 836cd2d1f..6b8595043 100644 --- a/tdesign-component/example/assets/api/result_api.md +++ b/tdesign-component/example/assets/api/result_api.md @@ -6,7 +6,19 @@ | --- | --- | --- | --- | | description | String? | - | 描述文本,用于提供额外信息 | | icon | Widget? | - | 图标组件,用于在结果中显示一个图标 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | theme | TResultTheme | TResultTheme.defaultTheme | 主题样式,默认主题样式为defaultTheme | | title | String | '' | 标题文本,显示结果的主要信息,默认标题为空字符串 | | titleStyle | TextStyle? | - | 自定义字体样式,用于设置标题文本的样式 | + + +### TResultTheme +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| defaultTheme | - | +| success | - | +| warning | - | +| error | - | diff --git a/tdesign-component/example/assets/api/search_api.md b/tdesign-component/example/assets/api/search_api.md index 08e6d630e..dd658ea95 100644 --- a/tdesign-component/example/assets/api/search_api.md +++ b/tdesign-component/example/assets/api/search_api.md @@ -14,7 +14,7 @@ | enabled | bool? | - | 是否禁用 | | focusNode | FocusNode? | - | 自定义焦点 | | inputAction | TextInputAction? | - | 键盘动作类型 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | mediumStyle | bool | false | 是否在导航栏中的样式 | | needCancel | bool | false | 是否需要取消按钮 | | onActionClick | TSearchBarEvent? | - | 自定义操作回调 | @@ -28,3 +28,51 @@ | placeHolder | String? | - | 预设文案 | | readOnly | bool? | - | 是否只读 | | style | TSearchStyle? | TSearchStyle.square | 样式 | + + +### TSearchStyle +#### 简介 +搜索框的样式 +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| square | 方形 | +| round | 圆形 | + + +### TSearchAlignment +#### 简介 +搜索框对齐方式 +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| left | 默认头部对齐 | +| center | 居中 | + + +### TSearchBarEvent +#### 类型定义 + +```dart +typedef TSearchBarEvent = void Function(String value); +``` + + +### TSearchBarClearEvent +#### 类型定义 + +```dart +typedef TSearchBarClearEvent = bool? Function(String value); +``` + + +### TSearchBarCallBack +#### 类型定义 + +```dart +typedef TSearchBarCallBack = void Function(); +``` diff --git a/tdesign-component/example/assets/api/side-bar_api.md b/tdesign-component/example/assets/api/side-bar_api.md index d36520c3d..76f810131 100644 --- a/tdesign-component/example/assets/api/side-bar_api.md +++ b/tdesign-component/example/assets/api/side-bar_api.md @@ -9,7 +9,7 @@ | controller | TSideBarController? | - | 控制器 | | defaultValue | int? | - | 默认值 | | height | double? | - | 高度 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | loading | bool? | - | 加载效果 | | loadingWidget | Widget? | - | 自定义加载动画 | | onChanged | ValueChanged? | - | 选中值发生变化(Controller控制) | @@ -22,8 +22,6 @@ | unSelectedColor | Color? | - | 未选中颜色 | | value | int? | - | 选项值 | -``` -``` ### TSideBarItem #### 默认构造方法 @@ -33,7 +31,17 @@ | badge | TBadge? | - | 徽标 | | disabled | bool | false | 是否禁用 | | icon | IconData? | - | 图标 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | label | String | '' | 标签 | | textStyle | TextStyle? | - | 标签样式 | | value | int | -1 | 值 | + + +### TSideBarStyle +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| normal | - | +| outline | - | diff --git a/tdesign-component/example/assets/api/skeleton_api.md b/tdesign-component/example/assets/api/skeleton_api.md index 7801ab935..5b13db47b 100644 --- a/tdesign-component/example/assets/api/skeleton_api.md +++ b/tdesign-component/example/assets/api/skeleton_api.md @@ -6,18 +6,29 @@ | --- | --- | --- | --- | | animation | TSkeletonAnimation? | - | 动画效果 | | delay | int | 0 | 延迟显示加载时间 | -| key | | - | | -| theme | | TSkeletonTheme.text | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | +| theme | TSkeletonTheme | TSkeletonTheme.text | - | + +#### 公开属性 + +| 属性 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| rowCol | TSkeletonRowCol | - | 自定义行列数量、宽度高度、间距等 | #### 工厂构造方法 -| 名称 | 说明 | -| --- | --- | -| TSkeleton.fromRowCol | 从行列框架创建骨架屏 | +##### TSkeleton.fromRowCol + +从行列框架创建骨架屏 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | +| animation | TSkeletonAnimation? | - | 动画效果 | +| delay | int | 0 | 延迟显示加载时间 | +| rowCol | TSkeletonRowCol | - | 自定义行列数量、宽度高度、间距等 | -``` -``` ### TSkeletonRowColStyle #### 默认构造方法 @@ -26,8 +37,6 @@ | --- | --- | --- | --- | | rowSpacing | double Function(BuildContext) | _defaultRowSpacing | 行间距 | -``` -``` ### TSkeletonRowCol #### 默认构造方法 @@ -37,8 +46,6 @@ | objects | List> | - | 行列对象 | | style | TSkeletonRowColStyle | const TSkeletonRowColStyle() | 样式 | -``` -``` ### TSkeletonRowColObjStyle #### 默认构造方法 @@ -51,15 +58,36 @@ #### 工厂构造方法 -| 名称 | 说明 | -| --- | --- | -| TSkeletonRowColObjStyle.circle | 圆形 | -| TSkeletonRowColObjStyle.rect | 矩形 | -| TSkeletonRowColObjStyle.spacer | 空白占位符 | -| TSkeletonRowColObjStyle.text | 文本 | +##### TSkeletonRowColObjStyle.circle + +圆形 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| background | Color Function(BuildContext) | _defaultBackground | 背景颜色 | + + +##### TSkeletonRowColObjStyle.rect + +矩形 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| background | Color Function(BuildContext) | _defaultBackground | 背景颜色 | + + +##### TSkeletonRowColObjStyle.spacer + +空白占位符 + +##### TSkeletonRowColObjStyle.text + +文本 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| background | Color Function(BuildContext) | _defaultBackground | 背景颜色 | -``` -``` ### TSkeletonRowColObj #### 默认构造方法 @@ -75,9 +103,78 @@ #### 工厂构造方法 -| 名称 | 说明 | -| --- | --- | -| TSkeletonRowColObj.circle | 圆形 | -| TSkeletonRowColObj.rect | 矩形 | -| TSkeletonRowColObj.spacer | 空白占位符 | -| TSkeletonRowColObj.text | 文本 | +##### TSkeletonRowColObj.circle + +圆形 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| width | double? | 48 | 宽度 | +| height | double? | 48 | 高度 | +| flex | int? | - | 弹性因子 | +| margin | EdgeInsets | EdgeInsets.zero | 间距 | +| style | TSkeletonRowColObjStyle | const TSkeletonRowColObjStyle.circle() | 样式 | + + +##### TSkeletonRowColObj.rect + +矩形 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| width | double? | - | 宽度 | +| height | double? | 16 | 高度 | +| flex | int? | 1 | 弹性因子 | +| margin | EdgeInsets | EdgeInsets.zero | 间距 | +| style | TSkeletonRowColObjStyle | const TSkeletonRowColObjStyle.rect() | 样式 | + + +##### TSkeletonRowColObj.spacer + +空白占位符 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| width | double? | - | 宽度 | +| height | double? | - | 高度 | +| flex | int? | - | 弹性因子 | +| margin | EdgeInsets | EdgeInsets.zero | 间距 | + + +##### TSkeletonRowColObj.text + +文本 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| width | double? | - | 宽度 | +| height | double? | 16 | 高度 | +| flex | int? | 1 | 弹性因子 | +| margin | EdgeInsets | EdgeInsets.zero | 间距 | +| style | TSkeletonRowColObjStyle | const TSkeletonRowColObjStyle.text() | 样式 | + + +### TSkeletonAnimation +#### 简介 +骨架图动画 +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| gradient | 渐变 | +| flashed | 闪烁 | + + +### TSkeletonTheme +#### 简介 +骨架图风格 +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| avatar | 头像 | +| image | 图片 | +| text | 文本 | +| paragraph | 段落 | diff --git a/tdesign-component/example/assets/api/slider_api.md b/tdesign-component/example/assets/api/slider_api.md index 7b00a6314..e7ee4332e 100644 --- a/tdesign-component/example/assets/api/slider_api.md +++ b/tdesign-component/example/assets/api/slider_api.md @@ -5,19 +5,17 @@ | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | boxDecoration | Decoration? | - | 自定义盒子样式 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | leftLabel | String? | - | 左侧标签 | -| onChanged | ValueChanged? | - | 滑动变化监听 | -| onChangeEnd | ValueChanged? | - | 滑动结束监听 | -| onChangeStart | ValueChanged? | - | 滑动开始监听 | -| onTap | Function(Position position, Offset offset, double value)? | - | Thumb 点击事件 位置、坐标、当前值 | -| onThumbTextTap | Function(Position position, Offset offset, double value)? | - | Thumb 点击浮标文字 位置、坐标、当前值 | +| onChanged | ValueChanged? | - | 滑动变化监听 | +| onChangeEnd | ValueChanged? | - | 滑动结束监听 | +| onChangeStart | ValueChanged? | - | 滑动开始监听 | +| onTap | Function(Offset offset, double value)? | - | Thumb 点击事件 坐标、当前值 | +| onThumbTextTap | Function(Offset offset, double value)? | - | Thumb 点击浮标文字 坐标、当前值 | | rightLabel | String? | - | 右侧标签 | | sliderThemeData | TSliderThemeData? | - | 样式 | -| value | RangeValues | - | 默认值 | +| value | double | - | 默认值 | -``` -``` ### TRangeSlider #### 默认构造方法 @@ -25,13 +23,23 @@ | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | boxDecoration | Decoration? | - | 自定义盒子样式 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | leftLabel | String? | - | 左侧标签 | | onChanged | ValueChanged? | - | 滑动变化监听 | | onChangeEnd | ValueChanged? | - | 滑动结束监听 | | onChangeStart | ValueChanged? | - | 滑动开始监听 | -| onTap | Function(Position position, Offset offset, double value)? | - | Thumb 点击事件 位置、坐标、当前值 | -| onThumbTextTap | Function(Position position, Offset offset, double value)? | - | Thumb 点击浮标文字 位置、坐标、当前值 | +| onTap | Function(Position position, Offset offset, double value)? | - | Thumb 点击事件 位置、坐标、当前值 | +| onThumbTextTap | Function(Position position, Offset offset, double value)? | - | Thumb 点击浮标文字 位置、坐标、当前值 | | rightLabel | String? | - | 右侧标签 | | sliderThemeData | TSliderThemeData? | - | 样式 | | value | RangeValues | - | 默认值 | + + +### Position +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| start | - | +| end | - | diff --git a/tdesign-component/example/assets/api/stepper_api.md b/tdesign-component/example/assets/api/stepper_api.md index 0ebfa602c..95debede2 100644 --- a/tdesign-component/example/assets/api/stepper_api.md +++ b/tdesign-component/example/assets/api/stepper_api.md @@ -10,7 +10,7 @@ | disableInput | bool | false | 禁用输入框 | | eventController | StreamController? | - | 事件控制器 | | inputWidth | double? | - | 禁用全部操作 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | max | int | 100 | 最大值 | | min | int | 0 | 最小值 | | onBlur | VoidCallback? | - | 输入框失去焦点时触发 | @@ -20,3 +20,70 @@ | step | int | 1 | 步长 | | theme | TStepperTheme | TStepperTheme.normal | 组件风格 | | value | int? | 0 | 值 | + + +### TStepperSize +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| small | - | +| medium | - | +| large | - | + + +### TStepperTheme +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| normal | - | +| filled | - | +| outline | - | + + +### TStepperIconType +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| remove | - | +| add | - | + + +### TStepperOverlimitType +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| minus | - | +| plus | - | + + +### TStepperEventType +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| cleanValue | - | + + +### TStepperOverlimitFunction +#### 类型定义 + +```dart +typedef TStepperOverlimitFunction = void Function(TStepperOverlimitType type); +``` + + +### TTapFunction +#### 类型定义 + +```dart +typedef TTapFunction = void Function(); +``` diff --git a/tdesign-component/example/assets/api/steps_api.md b/tdesign-component/example/assets/api/steps_api.md index 62115981f..6cd49f650 100644 --- a/tdesign-component/example/assets/api/steps_api.md +++ b/tdesign-component/example/assets/api/steps_api.md @@ -6,15 +6,13 @@ | --- | --- | --- | --- | | activeIndex | int | 0 | 步骤条当前激活的索引 | | direction | TStepsDirection | TStepsDirection.horizontal | 步骤条方向 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | readOnly | bool | false | 步骤条readOnly模式 | | simple | bool | false | 步骤条simple模式 | | status | TStepsStatus | TStepsStatus.success | 步骤条状态 | | steps | List | - | 步骤条数据 | | verticalSelect | bool | false | 步骤条垂直自定义步骤条选择模式 | -``` -``` ### TStepsItemData #### 默认构造方法 @@ -27,3 +25,27 @@ | errorIcon | IconData? | - | 失败图标 | | successIcon | IconData? | - | 成功图标 | | title | String? | - | 标题 | + + +### TStepsDirection +#### 简介 +Steps步骤条方向 +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| horizontal | - | +| vertical | - | + + +### TStepsStatus +#### 简介 +steps步骤条状态 +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| success | - | +| error | - | diff --git a/tdesign-component/example/assets/api/swipe-cell_api.md b/tdesign-component/example/assets/api/swipe-cell_api.md index b23f2a9e4..5f6f0c9ca 100644 --- a/tdesign-component/example/assets/api/swipe-cell_api.md +++ b/tdesign-component/example/assets/api/swipe-cell_api.md @@ -8,16 +8,16 @@ | --- | --- | --- | --- | | cell | Widget | - | 单元格 [TCell] | | closeWhenOpened | bool? | true | 当同一组([groupTag])中的一个[TSwipeCell]打开时,是否关闭组中的所有其他[TSwipeCell] | -| closeWhenTapped | bool? | true | 当同一组([groupTag])中的一个[TSwipeCell]被点击时,是否应该关闭组中的所有[TSwipeCell] | +| closeWhenTapped | bool? | true | 当同一组([groupTag])中的一个[TSwipeCell]被点击时,是否应该关闭组中的所有[TSwipeCell] [cell]组件被点击时必须传递点击事件,执行`TSwipeCellInherited.of(context)?.cellClick()` | | controller | SlidableController? | - | 自定义控制滑动窗口 | | direction | Axis? | Axis.horizontal | 可拖动的方向 | | disabled | bool? | false | 是否禁用滑动 | | dragStartBehavior | DragStartBehavior? | DragStartBehavior.start | 处理拖动开始行为的方式[GestureDetector.dragStartBehavior] | | duration | Duration? | const Duration(milliseconds: 200) | 打开关闭动画时长 | | groupTag | Object? | - | 组,配置后,[closeWhenOpened]、[closeWhenTapped]才起作用 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | left | TSwipeCellPanel? | - | 左侧滑动操作项面板 | -| onChange | Function(TSwipeDirection direction, bool open)? | - | 滑动展开事件 | +| onChange | Function(TSwipeDirection direction, bool open)? | - | 滑动展开事件 | | opened | List? | const [false, false] | 默认打开,[left, right] | | right | TSwipeCellPanel? | - | 右侧滑动操作项面板 | | slidableKey | Key? | - | 滑动组件的 Key | @@ -25,7 +25,36 @@ #### 静态方法 -| 名称 | 返回类型 | 参数 | 说明 | +##### TSwipeCell.close + +根据[groupTag]关闭[TSwipeCell] + + current:保留当前不关闭 + +返回类型:`void` + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| tag | Object? | - | - | +| current | SlidableController? | - | - | + + +##### TSwipeCell.of + +获取上下文最近的[controller] + +返回类型:`SlidableController?` + +| 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | -| close | | required Object? tag, SlidableController? current, | 根据[groupTag]关闭[TSwipeCell] current:保留当前不关闭 | -| of | | required BuildContext context, | 获取上下文最近的[controller] | +| context | BuildContext | - | - | + + +### TSwipeDirection +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| right | - | +| left | - | diff --git a/tdesign-component/example/assets/api/swiper_api.md b/tdesign-component/example/assets/api/swiper_api.md index 5a23bdff5..a6b8650eb 100644 --- a/tdesign-component/example/assets/api/swiper_api.md +++ b/tdesign-component/example/assets/api/swiper_api.md @@ -6,13 +6,20 @@ TDesign风格的Swiper指示器样式,与flutter_swiper的Swiper结合使用 | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | -| alignment | Alignment? | - | 当 scrollDirection== Axis.horizontal 时,默认Alignment.bottomCenter | +| alignment | Alignment? | - | 当 scrollDirection== Axis.horizontal 时,默认Alignment.bottomCenter 当 scrollDirection== Axis.vertical 时,默认Alignment.centerRight | | builder | SwiperPlugin | TSwiperPagination.dots | 具体样式 | -| key | Key? | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | margin | EdgeInsetsGeometry | const EdgeInsets.all(10.0) | 指示器和container之间的距离 | -``` -``` +#### 静态成员 + +| 名称 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| controls | SwiperPlugin | - | 箭头样式 | +| dots | SwiperPlugin | - | 圆点样式 | +| dotsBar | SwiperPlugin | - | 圆角矩形 + 圆点样式 默认宽度20,高度6 | +| fraction | SwiperPlugin | - | 数字样式 | + ### TPageTransformer #### 简介 @@ -28,7 +35,20 @@ TD默认PageTransformer #### 工厂构造方法 -| 名称 | 说明 | -| --- | --- | -| TPageTransformer.margin | 普通margin的卡片式 | -| TPageTransformer.scaleAndFade | 缩放或透明的卡片式 | +##### TPageTransformer.margin + +普通margin的卡片式 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| margin | double? | 6.0 | 左右间隔 | + + +##### TPageTransformer.scaleAndFade + +缩放或透明的卡片式 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| fade | double? | 1 | 淡化比例 | +| scale | double? | 0.8 | 缩放比例 | diff --git a/tdesign-component/example/assets/api/switch_api.md b/tdesign-component/example/assets/api/switch_api.md index fb8cb07d3..9491b21f0 100644 --- a/tdesign-component/example/assets/api/switch_api.md +++ b/tdesign-component/example/assets/api/switch_api.md @@ -7,7 +7,7 @@ | closeText | String? | - | 关闭文案 | | enable | bool | true | 是否可点击 | | isOn | bool | false | 是否打开 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | onChanged | OnSwitchChanged? | - | 改变事件 | | openText | String? | - | 打开文案 | | size | TSwitchSize? | TSwitchSize.medium | 尺寸:大、中、小 | @@ -18,3 +18,36 @@ | trackOffColor | Color? | - | 关闭时轨道颜色 | | trackOnColor | Color? | - | 开启时轨道颜色 | | type | TSwitchType? | TSwitchType.fill | 类型:填充、文本、加载 | + + +### TSwitchSize +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| large | - | +| medium | - | +| small | - | + + +### TSwitchType +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| fill | - | +| text | - | +| loading | - | +| icon | - | + + +### OnSwitchChanged +#### 简介 +开关改变事件处理 +#### 类型定义 + +```dart +typedef OnSwitchChanged = bool Function(bool value); +``` diff --git a/tdesign-component/example/assets/api/tab-bar_api.md b/tdesign-component/example/assets/api/tab-bar_api.md index dad57643b..84af968b6 100644 --- a/tdesign-component/example/assets/api/tab-bar_api.md +++ b/tdesign-component/example/assets/api/tab-bar_api.md @@ -4,11 +4,11 @@ | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | +| basicType | TBottomTabBarBasicType | - | 基本样式(纯文本、纯图标、图标+文本) | | animationCurve | Curve | Curves.easeInOutCubic | 动画曲线 | | animationDuration | Duration | const Duration(milliseconds: 300) | 动画时长 | | backgroundColor | Color? | - | 背景颜色 (可选) | | barHeight | double? | _kDefaultTabBarHeight | tab高度 | -| basicType | TBottomTabBarBasicType | basicType | 基本样式(纯文本、纯图标、图标+文本) | | centerDistance | double? | - | icon与文本中间距离(可选) | | componentType | TBottomTabBarComponentType? | TBottomTabBarComponentType.label | 选项样式 默认label | | currentIndex | int? | - | 选中的index(可选) | @@ -16,7 +16,7 @@ | dividerHeight | double? | - | 分割线高度(可选) | | dividerThickness | double? | - | 分割线厚度(可选) | | indicatorAnimation | TBottomTabBarIndicatorAnimation | TBottomTabBarIndicatorAnimation.none | 指示器动画类型 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | navigationTabs | List | - | tabs配置 | | needInkWell | bool | false | 是否需要水波纹效果 | | outlineType | TBottomTabBarOutlineType? | TBottomTabBarOutlineType.filled | 标签栏样式 默认filled | @@ -28,8 +28,6 @@ | useSafeArea | bool | true | 使用安全区域 | | useVerticalDivider | bool? | - | 是否使用竖线分隔(如果选项样式为 label,则强制为 false) | -``` -``` ### BadgeConfig #### 默认构造方法 @@ -41,8 +39,6 @@ | showBadge | bool | - | 是否展示消息 | | tBadge | TBadge? | - | 消息样式(未设置但 showBadge 为 true,则默认使用红点) | -``` -``` ### TBottomTabBarTabConfig #### 默认构造方法 @@ -60,8 +56,6 @@ | unselectedIcon | Widget? | - | 未选中时图标 | | unselectTabTextStyle | TextStyle? | - | 文本未选择样式 basicType为text时必填 | -``` -``` ### TBottomTabBarPopUpBtnConfig #### 默认构造方法 @@ -72,8 +66,6 @@ | onChanged | ValueChanged | - | 统一在 onChanged 中处理各item点击事件 | | popUpDialogConfig | TBottomTabBarPopUpShapeConfig? | - | 弹窗UI配置 | -``` -``` ### TBottomTabBarPopUpShapeConfig #### 默认构造方法 @@ -87,8 +79,6 @@ | popUpWidth | double? | - | 弹窗宽度(不设置,默认为按钮宽度 - 20) | | radius | double? | - | panel圆角 默认0 | -``` -``` ### PopUpMenuItem #### 默认构造方法 @@ -97,5 +87,48 @@ | --- | --- | --- | --- | | alignment | AlignmentGeometry | AlignmentDirectional.center | 对齐方式 | | itemWidget | Widget? | - | 选项widget | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | value | String | - | 选项值 | + + +### TBottomTabBarBasicType +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| text | 单层级纯文本标签栏 | +| iconText | 文本加图标标签栏 | +| icon | 纯图标标签栏 | +| expansionPanel | 双层级纯文本标签栏 | + + +### TBottomTabBarComponentType +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| normal | 普通样式 | +| label | 带胶囊背景的item选中样式 | + + +### TBottomTabBarOutlineType +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| filled | 填充样式 | +| capsule | 胶囊样式 | + + +### TBottomTabBarIndicatorAnimation +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| none | 无动画,瞬间切换 | +| linear | 线性滑动:指示器匀速从一个 tab 滑到另一个 | +| elastic | 弹性动画:指示器先拉伸后收缩 | diff --git a/tdesign-component/example/assets/api/table_api.md b/tdesign-component/example/assets/api/table_api.md index 30239b18d..b5b15b344 100644 --- a/tdesign-component/example/assets/api/table_api.md +++ b/tdesign-component/example/assets/api/table_api.md @@ -12,7 +12,7 @@ | empty | TTableEmpty? | - | 空表格呈现样式 | | footerWidget | Widget? | - | 自定义表尾 | | height | double? | - | 表格高度,超出后会出现滚动条 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | loading | bool? | false | 加载中状态 | | loadingWidget | Widget? | - | 自定义加载中状态 | | onCellTap | OnCellTap? | - | 单元格点击事件 | @@ -24,8 +24,6 @@ | stripe | bool? | false | 斑马纹 | | width | double? | - | 表格宽度 | -``` -``` ### TTableCol #### 默认构造方法 @@ -45,8 +43,6 @@ | title | String? | - | 表头标题 | | width | double? | - | 列宽 | -``` -``` ### TTableEmpty #### 默认构造方法 @@ -55,3 +51,73 @@ | --- | --- | --- | --- | | assetUrl | String? | - | 空状态图片 | | text | String? | - | 空状态文字 | + + +### TTableColFixed +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| left | - | +| right | - | +| none | - | + + +### TTableColAlign +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| left | - | +| center | - | +| right | - | + + +### SelectableFunc +#### 类型定义 + +```dart +typedef SelectableFunc = bool Function(int index, dynamic row); +``` + + +### RowCheckFunc +#### 类型定义 + +```dart +typedef RowCheckFunc = bool Function(int index, dynamic row); +``` + + +### OnCellTap +#### 类型定义 + +```dart +typedef OnCellTap = void Function(int rowIndex, dynamic row, TTableCol col); +``` + + +### OnScroll +#### 类型定义 + +```dart +typedef OnScroll = void Function(ScrollController controller); +``` + + +### OnSelect +#### 类型定义 + +```dart +typedef OnSelect = void Function(List? data); +``` + + +### OnRowSelect +#### 类型定义 + +```dart +typedef OnRowSelect = void Function(int index, bool checked); +``` diff --git a/tdesign-component/example/assets/api/tabs_api.md b/tdesign-component/example/assets/api/tabs_api.md index b55e32e45..2bddc3c38 100644 --- a/tdesign-component/example/assets/api/tabs_api.md +++ b/tdesign-component/example/assets/api/tabs_api.md @@ -16,24 +16,22 @@ | indicatorPadding | EdgeInsets? | - | 引导padding | | indicatorWidth | double? | - | tabBar下标宽度 | | isScrollable | bool | false | 是否滚动 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | labelColor | Color? | - | tabBar 已选标签颜色 | | labelPadding | EdgeInsetsGeometry? | - | tab间距 | | labelStyle | TextStyle? | - | 已选label字体 | -| onTap | Function(int)? | - | 点击事件 | +| onTap | Function(int)? | - | 点击事件 | | outlineType | TTabBarOutlineType | TTabBarOutlineType.filled | 选项卡样式 | | physics | ScrollPhysics? | - | 自定义滑动 | | selectedBgColor | Color? | - | 被选中背景色,只有outlineType为capsule时有效 | | showIndicator | bool | false | 是否展示引导控件 | -| tabAlignment | | - | | +| tabAlignment | TabAlignment? | - | - | | tabs | List | - | tab数组 | | unSelectedBgColor | Color? | - | 未选中背景色,只有outlineType为capsule时有效 | | unselectedLabelColor | Color? | - | tabBar未选标签颜色 | | unselectedLabelStyle | TextStyle? | - | unselectedLabel字体 | | width | double? | - | tabBar宽度 | -``` -``` ### TTab #### 默认构造方法 @@ -47,14 +45,12 @@ | height | double? | - | tab高度 | | icon | Widget? | - | 图标 | | iconMargin | EdgeInsetsGeometry | const EdgeInsets.only(bottom: 4.0, right: 4.0) | 图标间距 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | outlineType | TTabOutlineType | TTabOutlineType.filled | 选项卡样式 | | size | TTabSize | TTabSize.small | 选项卡尺寸 | | text | String? | - | 文字内容 | | textMargin | EdgeInsetsGeometry? | - | 中间内容宽度 | -``` -``` ### TTabBarView #### 默认构造方法 @@ -64,4 +60,36 @@ | children | List | - | 子widget列表 | | controller | TabController? | - | 控制器 | | isSlideSwitch | bool | false | 是否可以滑动切换 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | + + +### TTabSize +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| large | - | +| small | - | + + +### TTabOutlineType +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| filled | 填充样式 | +| capsule | 胶囊样式 | +| card | 卡片 | + + +### TTabBarOutlineType +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| filled | 填充样式 | +| capsule | 胶囊样式 | +| card | 卡片 | diff --git a/tdesign-component/example/assets/api/tag_api.md b/tdesign-component/example/assets/api/tag_api.md index 0493284b7..35b04aeab 100644 --- a/tdesign-component/example/assets/api/tag_api.md +++ b/tdesign-component/example/assets/api/tag_api.md @@ -4,6 +4,7 @@ | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | +| text | String | - | 标签内容 | | backgroundColor | Color? | - | 背景颜色,优先级高于style的backgroundColor | | disable | bool | false | 是否为禁用状态 | | fixedWidth | double? | - | 标签的固定宽度 | @@ -14,7 +15,7 @@ | iconWidget | Widget? | - | 自定义图标内容,需自处理颜色 | | isLight | bool | false | 是否为浅色 | | isOutline | bool | false | 是否为描边类型,默认不是 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | needCloseIcon | bool | false | 关闭图标 | | onCloseTap | GestureTapCallback? | - | 关闭图标点击事件 | | overflow | TextOverflow? | - | 文字溢出处理 | @@ -22,18 +23,16 @@ | shape | TTagShape | TTagShape.square | 标签形状 | | size | TTagSize | TTagSize.medium | 标签大小 | | style | TTagStyle? | - | 标签样式 | -| text | String | text | 标签内容 | | textColor | Color? | - | 文字颜色,优先级高于style的textColor | | theme | TTagTheme? | - | 主题 | -``` -``` ### TSelectTag #### 默认构造方法 | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | +| text | String | - | 标签内容 | | disableSelect | bool | false | 是否禁用选择 | | disableSelectStyle | TTagStyle? | - | 不可选标签样式 | | fixedWidth | double? | - | 标签的固定宽度 | @@ -43,7 +42,7 @@ | isLight | bool | false | 是否为浅色 | | isOutline | bool | false | 是否为描边类型,默认不是 | | isSelected | bool | false | 是否选中 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | needCloseIcon | bool | false | 关闭图标 | | onCloseTap | GestureTapCallback? | - | 关闭图标点击事件 | | onSelectChanged | ValueChanged? | - | 标签点击,选中状态改变时的回调 | @@ -51,12 +50,9 @@ | selectStyle | TTagStyle? | - | 选中的标签样式 | | shape | TTagShape | TTagShape.square | 标签形状 | | size | TTagSize | TTagSize.medium | 标签大小 | -| text | String | text | 标签内容 | | theme | TTagTheme? | - | 主题 | | unSelectStyle | TTagStyle? | - | 未选中标签样式 | -``` -``` ### TTagStyle #### 默认构造方法 @@ -72,11 +68,89 @@ | fontWeight | FontWeight? | - | 字体粗细 | | textColor | Color? | - | 文字颜色 | +#### 公开属性 + +| 属性 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| closeIconColor | Color? | - | 关闭图标颜色 | + #### 工厂构造方法 -| 名称 | 说明 | -| --- | --- | -| TTagStyle.generateDisableSelectStyle | 根据主题生成禁用Tag样式 | -| TTagStyle.generateFillStyleByTheme | 根据主题生成填充Tag样式 | -| TTagStyle.generateOutlineStyleByTheme | 根据主题生成描边Tag样式 | +##### TTagStyle.generateDisableSelectStyle + +根据主题生成禁用Tag样式 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| context | BuildContext | - | 上下文,方便获取主题内容 | +| isLight | bool | - | - | +| isOutline | bool | - | - | +| shape | TTagShape | - | - | + + +##### TTagStyle.generateFillStyleByTheme + +根据主题生成填充Tag样式 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| context | BuildContext | - | 上下文,方便获取主题内容 | +| theme | TTagTheme? | - | - | +| light | bool | - | - | +| shape | TTagShape | - | - | + + +##### TTagStyle.generateOutlineStyleByTheme + +根据主题生成描边Tag样式 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| context | BuildContext | - | 上下文,方便获取主题内容 | +| theme | TTagTheme? | - | - | +| light | bool | - | - | +| shape | TTagShape | - | - | + + +### TTagTheme +#### 简介 +Tag展示类型 +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| defaultTheme | 默认 | +| primary | 常规 | +| warning | 警告 | +| danger | 危险 | +| success | 成功 | + + +### TTagSize +#### 简介 +标签尺寸 +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| extraLarge | - | +| large | - | +| medium | - | +| small | - | +| custom | - | + + +### TTagShape +#### 简介 +标签形状 +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| square | - | +| round | - | +| mark | - | diff --git a/tdesign-component/example/assets/api/text_api.md b/tdesign-component/example/assets/api/text_api.md index e394cf480..07aaf746c 100644 --- a/tdesign-component/example/assets/api/text_api.md +++ b/tdesign-component/example/assets/api/text_api.md @@ -4,8 +4,8 @@ | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | +| data | - | - | 以下系统 text 属性,释义请参考系统 [Text] 中注释 | | backgroundColor | Color? | - | 背景颜色 | -| data | null | data | 以下系统 text 属性,释义请参考系统 [Text] 中注释 | | font | Font? | - | 字体尺寸,包含 大小size 和 行高height | | fontFamily | FontFamily? | - | 字体ttf | | fontFamilyUrl | String? | - | 是否禁用懒加载 FontFamily 的能力 | @@ -13,64 +13,94 @@ | forceVerticalCenter | bool | false | 是否强制居中 | | isInFontLoader | bool | false | 是否在 FontLoader 中使用 | | isTextThrough | bool? | false | 是否是横线穿过样式(删除线) | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | lineThroughColor | Color? | - | 删除线颜色,对应 TestStyle 的 decorationColor | -| locale | | - | | -| maxLines | | - | | -| overflow | | - | | +| locale | Locale? | - | - | +| maxLines | int? | - | - | +| overflow | TextOverflow? | - | - | | package | String? | - | 字体包名 | -| semanticsLabel | | - | | -| softWrap | | - | | -| strutStyle | | - | | +| semanticsLabel | String? | - | - | +| softWrap | bool? | - | - | +| strutStyle | StrutStyle? | - | - | | style | TextStyle? | - | 自定义的 TextStyle,其中指定的属性,将覆盖扩展的外层属性 | -| textAlign | | - | | +| textAlign | TextAlign? | - | - | | textColor | Color? | - | 文本颜色 | -| textDirection | | - | | -| textHeightBehavior | | - | | -| textScaleFactor | | - | | -| textWidthBasis | | - | | +| textDirection | TextDirection? | - | - | +| textHeightBehavior | ui.TextHeightBehavior? | - | - | +| textScaleFactor | double? | - | - | +| textWidthBasis | TextWidthBasis? | - | - | + +#### 公开属性 + +| 属性 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| textSpan | InlineSpan? | - | - | #### 工厂构造方法 -| 名称 | 说明 | -| --- | --- | -| TText.rich | 富文本构造方法 | +##### TText.rich + +富文本构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| textSpan | InlineSpan? | - | - | +| font | Font? | - | 字体尺寸,包含 大小size 和 行高height | +| fontWeight | FontWeight? | - | 字体粗细 | +| fontFamily | FontFamily? | - | 字体ttf | +| textColor | Color? | - | 文本颜色 | +| backgroundColor | Color? | - | 背景颜色 | +| isTextThrough | bool? | false | 是否是横线穿过样式(删除线) | +| lineThroughColor | Color? | - | 删除线颜色,对应 TestStyle 的 decorationColor | +| package | String? | - | 字体包名 | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | +| style | TextStyle? | - | 自定义的 TextStyle,其中指定的属性,将覆盖扩展的外层属性 | +| strutStyle | StrutStyle? | - | - | +| textAlign | TextAlign? | - | - | +| textDirection | TextDirection? | - | - | +| locale | Locale? | - | - | +| softWrap | bool? | - | - | +| overflow | TextOverflow? | - | - | +| textScaleFactor | double? | - | - | +| maxLines | int? | - | - | +| semanticsLabel | String? | - | - | +| textWidthBasis | TextWidthBasis? | - | - | +| textHeightBehavior | ui.TextHeightBehavior? | - | - | +| forceVerticalCenter | bool | false | 是否强制居中 | +| isInFontLoader | bool | false | 是否在 FontLoader 中使用 | +| fontFamilyUrl | String? | - | 是否禁用懒加载 FontFamily 的能力 | -``` -``` ### TTextSpan #### 默认构造方法 | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | -| children | | - | | -| context | | - | | -| font | | - | | -| fontFamily | | - | | -| fontWeight | | - | | -| isTextThrough | | false | | -| lineThroughColor | | - | | -| mouseCursor | | - | | -| onEnter | | - | | -| onExit | | - | | -| package | | - | | -| recognizer | | - | | -| semanticsLabel | | - | | -| style | | - | | -| text | | - | | -| textColor | | - | | +| children | List? | - | - | +| context | BuildContext? | - | - | +| font | Font? | - | - | +| fontFamily | FontFamily? | - | - | +| fontWeight | FontWeight? | - | - | +| isTextThrough | bool? | false | - | +| lineThroughColor | Color? | - | - | +| mouseCursor | MouseCursor? | - | - | +| onEnter | PointerEnterEventListener? | - | - | +| onExit | PointerExitEventListener? | - | - | +| package | String? | - | - | +| recognizer | GestureRecognizer? | - | - | +| semanticsLabel | String? | - | - | +| style | TextStyle? | - | - | +| text | String? | - | - | +| textColor | Color? | - | - | -``` -``` ### TTextConfiguration #### 默认构造方法 | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | -| child | | - | | +| child | Widget | - | - | | globalFontFamily | FontFamily? | - | 全局字体,kTextNeedGlobalFontFamily=true 时生效 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | paddingConfig | TTextPaddingConfig? | - | forceVerticalCenter=true 时,内置 padding 配置 | diff --git a/tdesign-component/example/assets/api/textarea_api.md b/tdesign-component/example/assets/api/textarea_api.md index f131dad57..0414512df 100644 --- a/tdesign-component/example/assets/api/textarea_api.md +++ b/tdesign-component/example/assets/api/textarea_api.md @@ -23,7 +23,7 @@ | inputDecoration | InputDecoration? | - | 自定义输入框TextField组件样式 | | inputFormatters | List? | - | 显示输入内容,如限制长度(LengthLimitingTextInputFormatter(6)) | | inputType | TextInputType? | - | 键盘类型,数字、字母 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | label | String? | - | 输入框标题 | | labelIcon | Widget? | - | 输入框标题图标 | | labelStyle | TextStyle? | - | 左侧标签文本样式 | @@ -48,3 +48,13 @@ | textInputBackgroundColor | Color? | - | 文本框背景色 | | textStyle | TextStyle? | - | 文本颜色 | | width | double? | - | 输入框宽度 | + + +### TTextareaLayout +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| vertical | - | +| horizontal | - | diff --git a/tdesign-component/example/assets/api/theme_api.md b/tdesign-component/example/assets/api/theme_api.md index 3dd48fc58..922b3d8be 100644 --- a/tdesign-component/example/assets/api/theme_api.md +++ b/tdesign-component/example/assets/api/theme_api.md @@ -6,22 +6,66 @@ | --- | --- | --- | --- | | child | Widget | - | 子控件 | | data | TThemeData | - | 主题数据 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | systemData | ThemeData? | - | Flutter系统主题数据 | #### 静态方法 -| 名称 | 返回类型 | 参数 | 说明 | +##### TTheme.defaultData + +获取默认主题数据,全局唯一 + +返回类型:`TThemeData` + +##### TTheme.needMultiTheme + +开启多套主题功能 + +返回类型:`void` + +| 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | -| defaultData | | | 获取默认主题数据,全局唯一 | -| needMultiTheme | | bool value, | 开启多套主题功能 | -| of | | BuildContext? context, | 获取主题数据,如果未传context则获取全局唯一的默认数据, 传了context,则获取最近的主题,取不到则会获取全局唯一默认数据 | -| ofNullable | | BuildContext? context, | 获取主题数据,取不到则可空 传了context,则获取最近的主题,取不到或未传context则返回null, | -| setResourceBuilder | | required TResourceBuilder delegate, bool needAlwaysBuild, | 设置资源代理, needAlwaysBuild=true:每次都会走build方法;如果全局有多个Delegate,需要区分情况去获取,则可以设置needAlwaysBuild为true,业务自己判断返回哪个delegate needAlwaysBuild=false:返回delegate为null,则每次都会走build方法,返回了 | +| value | bool | true | - | + + +##### TTheme.of + +获取主题数据,如果未传context则获取全局唯一的默认数据, + 传了context,则获取最近的主题,取不到则会获取全局唯一默认数据 + +返回类型:`TThemeData` + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| context | BuildContext? | - | - | + + +##### TTheme.ofNullable + +获取主题数据,取不到则可空 + 传了context,则获取最近的主题,取不到或未传context则返回null, + +返回类型:`TThemeData?` + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| context | BuildContext? | - | - | + + +##### TTheme.setResourceBuilder + +设置资源代理, + needAlwaysBuild=true:每次都会走build方法;如果全局有多个Delegate,需要区分情况去获取,则可以设置needAlwaysBuild为true,业务自己判断返回哪个delegate + needAlwaysBuild=false:返回delegate为null,则每次都会走build方法,返回了 + +返回类型:`void` + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| delegate | TResourceBuilder | - | - | +| needAlwaysBuild | bool | false | - | -``` -``` ### TThemeData #### 默认构造方法 @@ -38,11 +82,64 @@ | shadowMap | TMap> | - | 阴影 | | spacerMap | TMap | - | 间隔 | +#### 公开属性 + +| 属性 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| dark | TThemeData? | - | 暗色主题 | +| light | TThemeData | - | 亮色主题 | + #### 静态方法 -| 名称 | 返回类型 | 参数 | 说明 | +##### TThemeData.defaultData + +获取默认Data,一个App里只有一个,用于没有context的地方 + +返回类型:`TThemeData` + +| 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | -| defaultData | | TExtraThemeData? extraThemeData, | 获取默认Data,一个App里只有一个,用于没有context的地方 | -| fromJson | | required String name, required String themeJson, String? darkName, null recoverDefault, TExtraThemeData? extraThemeData, | 解析配置的json文件为主题数据 [name] 主题名称,目前只支持一级键 [themeJson] 主题json字符串,要求json配置必须正确 [recoverDefault] 是否恢复为默认主题数据 [extraThemeData] 额外扩展的主题数据 | -| parseThemeData | | required String name, required null themeConfig, required TExtraThemeData? extraThemeData, | | +| extraThemeData | TExtraThemeData? | - | 额外定义的结构 | + + +##### TThemeData.fromJson + +解析配置的json文件为主题数据 + + [name] 主题名称,目前只支持一级键 + + [themeJson] 主题json字符串,要求json配置必须正确 + + [recoverDefault] 是否恢复为默认主题数据 + + [extraThemeData] 额外扩展的主题数据 + +返回类型:`TThemeData?` + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| name | String | - | 名称 | +| themeJson | String | - | - | +| darkName | String? | - | - | +| recoverDefault | - | false | - | +| extraThemeData | TExtraThemeData? | - | 额外定义的结构 | + + +##### TThemeData.parseThemeData + +返回类型:`TThemeData` + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| name | String | - | 名称 | +| themeConfig | - | - | - | +| extraThemeData | TExtraThemeData? | - | 额外定义的结构 | + + +### DefaultMapFactory +#### 类型定义 + +```dart +typedef DefaultMapFactory = TMap? Function(); +``` diff --git a/tdesign-component/example/assets/api/time-counter_api.md b/tdesign-component/example/assets/api/time-counter_api.md index 574d84ddb..5c81242ea 100644 --- a/tdesign-component/example/assets/api/time-counter_api.md +++ b/tdesign-component/example/assets/api/time-counter_api.md @@ -11,9 +11,9 @@ | controller | TTimeCounterController? | - | 控制器,可控制开始/暂停/继续/重置 | | direction | TTimeCounterDirection | TTimeCounterDirection.down | 计时方向,默认倒计时 | | format | String | 'HH:mm:ss' | 时间格式,DD-日,HH-时,mm-分,ss-秒,SSS-毫秒(分隔符必须为长度为1的非空格的字符) | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | millisecond | bool | false | 是否开启毫秒级渲染 | -| onChange | Function(int time)? | - | 时间变化时触发回调 | +| onChange | Function(int time)? | - | 时间变化时触发回调 | | onFinish | VoidCallback? | - | 计时结束时触发回调 | | size | TTimeCounterSize | TTimeCounterSize.medium | 尺寸 | | splitWithUnit | bool | false | 使用时间单位分割 | @@ -21,14 +21,10 @@ | theme | TTimeCounterTheme | TTimeCounterTheme.defaultTheme | 风格 | | time | int | - | 必需;计时时长,单位毫秒 | -``` -``` ### TTimeCounterController #### 简介 倒计时组件控制器,可控制开始(`start()`)/暂停(`pause()`)/继续(`resume()`)/重置(`reset([int? time])`) -``` -``` ### TTimeCounterStyle #### 简介 @@ -56,6 +52,66 @@ #### 工厂构造方法 -| 名称 | 说明 | -| --- | --- | -| TTimeCounterStyle.generateStyle | 生成默认样式 | +##### TTimeCounterStyle.generateStyle + +生成默认样式 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| context | BuildContext | - | - | +| size | TTimeCounterSize? | - | - | +| theme | TTimeCounterTheme? | - | - | +| splitWithUnit | bool? | - | - | + + +### TTimeCounterStatus +#### 简介 +计时组件控制器转态 +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| start | 开始 | +| pause | 暂停 | +| resume | 继续 | +| reset | 重置 | +| idle | 空,默认值 | + + +### TTimeCounterDirection +#### 简介 +计时组件计时方向 +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| down | 倒计时 | +| up | 正向计时 | + + +### TTimeCounterSize +#### 简介 +计时组件尺寸 +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| small | 小 | +| medium | 中等 | +| large | 大 | + + +### TTimeCounterTheme +#### 简介 +计时组件风格 +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| defaultTheme | 默认 | +| round | 圆形 | +| square | 方形 | diff --git a/tdesign-component/example/assets/api/toast_api.md b/tdesign-component/example/assets/api/toast_api.md index fd2a9575d..f0699ef24 100644 --- a/tdesign-component/example/assets/api/toast_api.md +++ b/tdesign-component/example/assets/api/toast_api.md @@ -3,15 +3,176 @@ #### 静态方法 -| 名称 | 返回类型 | 参数 | 说明 | -| --- | --- | --- | --- | -| dismissAll | | | 关闭所有Toast | -| dismissLoading | | | 关闭加载Toast(向后兼容) | -| dismissToast | | required String toastId, | 关闭指定的Toast | -| showFail | | required String? text, IconTextDirection direction, required BuildContext context, Duration duration, bool? preventTap, Color? backgroundColor, int? maxLines, TextStyle? textStyle, double? iconSize, Color? iconColor, String? toastId, | 失败提示Toast | -| showIconText | | required String? text, IconData? icon, IconTextDirection direction, required BuildContext context, Duration duration, bool? preventTap, Color? backgroundColor, int? maxLines, TextStyle? textStyle, double? iconSize, Color? iconColor, String? toastId, | 带图标的Toast | -| showLoading | | required BuildContext context, String? text, Duration duration, bool? preventTap, Widget? customWidget, Color? backgroundColor, TextStyle? textStyle, double? iconSize, Color? iconColor, String? toastId, | 带文案的加载Toast | -| showLoadingWithoutText | | required BuildContext context, Duration duration, bool? preventTap, Color? backgroundColor, double? iconSize, Color? iconColor, String? toastId, | 不带文案的加载Toast | -| showSuccess | | required String? text, IconTextDirection direction, required BuildContext context, Duration duration, bool? preventTap, Color? backgroundColor, int? maxLines, TextStyle? textStyle, double? iconSize, Color? iconColor, String? toastId, | 成功提示Toast | -| showText | | required String? text, required BuildContext context, Duration duration, int? maxLines, BoxConstraints? constraints, bool? preventTap, Widget? customWidget, Color? backgroundColor, TextStyle? textStyle, String? toastId, | 普通文本Toast | -| showWarning | | required String? text, IconTextDirection direction, required BuildContext context, Duration duration, bool? preventTap, Color? backgroundColor, int? maxLines, TextStyle? textStyle, double? iconSize, Color? iconColor, String? toastId, | 警告Toast | +##### TToast.dismissAll + +关闭所有Toast + +返回类型:`void` + +##### TToast.dismissLoading + +关闭加载Toast(向后兼容) + +返回类型:`void` + +##### TToast.dismissToast + +关闭指定的Toast + +返回类型:`void` + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| toastId | String | - | - | + + +##### TToast.showFail + +失败提示Toast + +返回类型:`String` + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| text | String? | - | - | +| direction | IconTextDirection | IconTextDirection.horizontal | - | +| context | BuildContext | - | - | +| duration | Duration | const Duration(milliseconds: 3000) | - | +| preventTap | bool? | - | - | +| backgroundColor | Color? | - | - | +| maxLines | int? | - | - | +| textStyle | TextStyle? | - | - | +| iconSize | double? | - | - | +| iconColor | Color? | - | - | +| toastId | String? | - | - | + + +##### TToast.showIconText + +带图标的Toast + +返回类型:`String` + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| text | String? | - | - | +| icon | IconData? | - | - | +| direction | IconTextDirection | IconTextDirection.horizontal | - | +| context | BuildContext | - | - | +| duration | Duration | const Duration(milliseconds: 3000) | - | +| preventTap | bool? | - | - | +| backgroundColor | Color? | - | - | +| maxLines | int? | - | - | +| textStyle | TextStyle? | - | - | +| iconSize | double? | - | - | +| iconColor | Color? | - | - | +| toastId | String? | - | - | + + +##### TToast.showLoading + +带文案的加载Toast + +返回类型:`String` + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| context | BuildContext | - | - | +| text | String? | - | - | +| duration | Duration | const Duration(seconds: 99999999) | - | +| preventTap | bool? | - | - | +| customWidget | Widget? | - | - | +| backgroundColor | Color? | - | - | +| textStyle | TextStyle? | - | - | +| iconSize | double? | - | - | +| iconColor | Color? | - | - | +| toastId | String? | - | - | + + +##### TToast.showLoadingWithoutText + +不带文案的加载Toast + +返回类型:`String` + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| context | BuildContext | - | - | +| duration | Duration | const Duration(seconds: 99999999) | - | +| preventTap | bool? | - | - | +| backgroundColor | Color? | - | - | +| iconSize | double? | - | - | +| iconColor | Color? | - | - | +| toastId | String? | - | - | + + +##### TToast.showSuccess + +成功提示Toast + +返回类型:`String` + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| text | String? | - | - | +| direction | IconTextDirection | IconTextDirection.horizontal | - | +| context | BuildContext | - | - | +| duration | Duration | const Duration(milliseconds: 3000) | - | +| preventTap | bool? | - | - | +| backgroundColor | Color? | - | - | +| maxLines | int? | - | - | +| textStyle | TextStyle? | - | - | +| iconSize | double? | - | - | +| iconColor | Color? | - | - | +| toastId | String? | - | - | + + +##### TToast.showText + +普通文本Toast + +返回类型:`String` + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| text | String? | - | - | +| context | BuildContext | - | - | +| duration | Duration | const Duration(milliseconds: 3000) | - | +| maxLines | int? | - | - | +| constraints | BoxConstraints? | - | - | +| preventTap | bool? | - | - | +| customWidget | Widget? | - | - | +| backgroundColor | Color? | - | - | +| textStyle | TextStyle? | - | - | +| toastId | String? | - | - | + + +##### TToast.showWarning + +警告Toast + +返回类型:`String` + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| text | String? | - | - | +| direction | IconTextDirection | IconTextDirection.horizontal | - | +| context | BuildContext | - | - | +| duration | Duration | const Duration(milliseconds: 3000) | - | +| preventTap | bool? | - | - | +| backgroundColor | Color? | - | - | +| maxLines | int? | - | - | +| textStyle | TextStyle? | - | - | +| iconSize | double? | - | - | +| iconColor | Color? | - | - | +| toastId | String? | - | - | + + +### IconTextDirection +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| horizontal | 横向 | +| vertical | 竖向 | diff --git a/tdesign-component/example/assets/api/tree-select_api.md b/tdesign-component/example/assets/api/tree-select_api.md index 7ac293da2..ddb46646a 100644 --- a/tdesign-component/example/assets/api/tree-select_api.md +++ b/tdesign-component/example/assets/api/tree-select_api.md @@ -6,15 +6,13 @@ | --- | --- | --- | --- | | defaultValue | List | const [] | 初始值,对应options中的value值 | | height | double | 336 | 高度 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | multiple | bool | false | 支持多选 | | onChange | TTreeSelectChangeEvent? | - | 选中值发生变化 | | options | List | const [] | 展示的选项列表 | | outwardCornerRadius | double | 9 | 一级菜单选中项的外弯折圆角半径,默认为 9 | | style | TTreeSelectStyle | TTreeSelectStyle.normal | 一级菜单样式 | -``` -``` ### TSelectOption #### 默认构造方法 @@ -27,3 +25,21 @@ | maxLines | int | 1 | 最大显示行数 | | multiple | bool | false | 当前子项支持多选 | | value | dynamic | - | 值 | + + +### TTreeSelectStyle +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| normal | - | +| outline | - | + + +### TTreeSelectChangeEvent +#### 类型定义 + +```dart +typedef TTreeSelectChangeEvent = void Function(List, int level); +``` diff --git a/tdesign-component/example/assets/api/upload_api.md b/tdesign-component/example/assets/api/upload_api.md index 978ac3fc3..52cd31835 100644 --- a/tdesign-component/example/assets/api/upload_api.md +++ b/tdesign-component/example/assets/api/upload_api.md @@ -8,7 +8,7 @@ | enabledReplaceType | bool? | false | 是否启用replace功能 | | files | List | - | 控制展示的文件列表 | | height | double? | 80.0 | 图片高度 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | max | int | 0 | 用于控制文件上传数量,0为不限制,仅在multiple为true时有效 | | mediaType | List | const [TUploadMediaType.image, TUploadMediaType.video] | 支持上传的文件类型,图片或视频 | | multiple | bool | false | 是否多选上传,默认false | @@ -25,3 +25,88 @@ | wrapAlignment | WrapAlignment? | - | 多图对齐方式 | | wrapRunSpacing | double? | - | 多图布局时的 runSpacing | | wrapSpacing | double? | - | 多图布局时的 spacing | + + +### TUploadMediaType +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| image | - | +| video | - | + + +### TUploadValidatorError +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| overSize | - | +| overQuantity | - | + + +### TUploadFileStatus +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| success | - | +| loading | - | +| error | - | +| retry | - | + + +### TUploadType +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| add | - | +| remove | - | +| replace | - | + + +### TUploadBoxType +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| roundedSquare | - | +| circle | - | + + +### TUploadErrorEvent +#### 类型定义 + +```dart +typedef TUploadErrorEvent = void Function(Object e); +``` + + +### TUploadClickEvent +#### 类型定义 + +```dart +typedef TUploadClickEvent = void Function(int value); +``` + + +### TUploadValueChangedEvent +#### 类型定义 + +```dart +typedef TUploadValueChangedEvent = void Function(List files, TUploadType type); +``` + + +### TUploadValidatorEvent +#### 类型定义 + +```dart +typedef TUploadValidatorEvent = void Function(TUploadValidatorError e); +``` diff --git a/tdesign-component/example/assets/code/indexes._buildCustomIndexes.txt b/tdesign-component/example/assets/code/indexes._buildCustomIndexes.txt index afd087518..ffda89e81 100644 --- a/tdesign-component/example/assets/code/indexes._buildCustomIndexes.txt +++ b/tdesign-component/example/assets/code/indexes._buildCustomIndexes.txt @@ -9,16 +9,16 @@ Widget _buildCustomIndexes(BuildContext context) { theme: TButtonTheme.primary, type: TButtonType.outline, onTap: () { - Navigator.of(context).push( - TSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.right, - modalTop: renderBox?.size.height, - builder: (context) { - return TIndexes( + TPopup.show( + context, + options: TPopupOptions.right( + width: 280, + inset: TPopupRightInset(top: renderBox?.size.height ?? 0), + child: TIndexes( indexList: indexList, builderIndex: (context, index, isActive) { return TText( - '自定义 ${index}', + '自定义 $index', textColor: isActive ? TTheme.of(context).brandNormalColor : TTheme.of(context).textColorPrimary, @@ -29,16 +29,10 @@ Widget _buildCustomIndexes(BuildContext context) { (element) => element['index'] == index)['children'] as List; return TCellGroup( - cells: list - .map((e) => TCell( - title: e, - )) - .toList(), + cells: list.map((e) => TCell(title: e)).toList(), ); }, - ); - }, - ), + )), ); }, ); diff --git a/tdesign-component/example/assets/code/indexes._buildOther.txt b/tdesign-component/example/assets/code/indexes._buildOther.txt index 64b0446c9..0af8b5ceb 100644 --- a/tdesign-component/example/assets/code/indexes._buildOther.txt +++ b/tdesign-component/example/assets/code/indexes._buildOther.txt @@ -9,12 +9,12 @@ Widget _buildOther(BuildContext context) { theme: TButtonTheme.primary, type: TButtonType.outline, onTap: () { - Navigator.of(context).push( - TSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.right, - modalTop: renderBox?.size.height, - builder: (context) { - return TIndexes( + TPopup.show( + context, + options: TPopupOptions.right( + width: 280, + inset: TPopupRightInset(top: renderBox?.size.height ?? 0), + child: TIndexes( indexList: indexList, capsuleTheme: true, builderContent: (context, index) { @@ -22,16 +22,10 @@ Widget _buildOther(BuildContext context) { (element) => element['index'] == index)['children'] as List; return TCellGroup( - cells: list - .map((e) => TCell( - title: e, - )) - .toList(), + cells: list.map((e) => TCell(title: e)).toList(), ); }, - ); - }, - ), + )), ); }, ); diff --git a/tdesign-component/example/assets/code/indexes._buildSimple.txt b/tdesign-component/example/assets/code/indexes._buildSimple.txt index 5cec08001..93f3f3816 100644 --- a/tdesign-component/example/assets/code/indexes._buildSimple.txt +++ b/tdesign-component/example/assets/code/indexes._buildSimple.txt @@ -9,28 +9,22 @@ Widget _buildSimple(BuildContext context) { theme: TButtonTheme.primary, type: TButtonType.outline, onTap: () { - Navigator.of(context).push( - TSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.right, - modalTop: renderBox?.size.height, - builder: (context) { - return TIndexes( + TPopup.show( + context, + options: TPopupOptions.right( + width: 280, + inset: TPopupRightInset(top: renderBox?.size.height ?? 0), + child: TIndexes( indexList: indexList, builderContent: (context, index) { final list = _list.firstWhere( (element) => element['index'] == index)['children'] as List; return TCellGroup( - cells: list - .map((e) => TCell( - title: e, - )) - .toList(), + cells: list.map((e) => TCell(title: e)).toList(), ); }, - ); - }, - ), + )), ); }, ); diff --git a/tdesign-component/example/assets/code/picker.buildCustomItemBuilder.txt b/tdesign-component/example/assets/code/picker.buildCustomItemBuilder.txt index c8c74463e..0e74fc62e 100644 --- a/tdesign-component/example/assets/code/picker.buildCustomItemBuilder.txt +++ b/tdesign-component/example/assets/code/picker.buildCustomItemBuilder.txt @@ -5,12 +5,14 @@ children: [ Text( '示例:itemBuilder 自定义子项渲染,可添加图标、背景色等', - style: TextStyle(fontSize: 12, color: TTheme.of(context).textColorPlaceholder), + style: TextStyle( + fontSize: 12, color: TTheme.of(context).textColorPlaceholder), ), const SizedBox(height: 4), Text( '选中: ${_customItemBuilderValue.isEmpty ? "未选择" : _customItemBuilderValue}', - style: TextStyle(fontSize: 14, color: TTheme.of(context).textColorSecondary), + style: TextStyle( + fontSize: 14, color: TTheme.of(context).textColorSecondary), ), const SizedBox(height: 8), _pickerCard( @@ -36,15 +38,19 @@ content, style: TextStyle( fontSize: 16, - fontWeight: selected ? FontWeight.w600 : FontWeight.normal, - color: selected ? theme.brandNormalColor : theme.fontGyColor1, + fontWeight: + selected ? FontWeight.w600 : FontWeight.normal, + color: selected + ? theme.brandNormalColor + : theme.fontGyColor1, ), ), ], ), ); }, - onChange: (v) => setState(() => _customItemBuilderValue = v.labels.first), + onChange: (v) => + setState(() => _customItemBuilderValue = v.labels.first), ), ), ], diff --git a/tdesign-component/example/assets/code/picker.buildCustomKeys.txt b/tdesign-component/example/assets/code/picker.buildCustomKeys.txt index 6c8f8dd8a..b7012897c 100644 --- a/tdesign-component/example/assets/code/picker.buildCustomKeys.txt +++ b/tdesign-component/example/assets/code/picker.buildCustomKeys.txt @@ -1,19 +1,22 @@ Widget buildCustomKeys(BuildContext context) { // 用 keys 告诉组件「city 映射为 label,code 是 value,readonly 是 disabled」 - const keys = TPickerKeys(label: 'city', value: 'code', disabled: 'readonly'); + const keys = + TPickerKeys(label: 'city', value: 'code', disabled: 'readonly'); final label = _customKeysValue?.labels.join() ?? ''; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '后端原始字段:city / code / readonly。通过 keys(label: "city") 映射为 label', - style: TextStyle(fontSize: 12, color: TTheme.of(context).textColorPlaceholder), + style: TextStyle( + fontSize: 12, color: TTheme.of(context).textColorPlaceholder), ), const SizedBox(height: 4), Text( '当前选中:${label.isEmpty ? "未选择" : label}', - style: TextStyle(fontSize: 14, color: TTheme.of(context).textColorSecondary), + style: TextStyle( + fontSize: 14, color: TTheme.of(context).textColorSecondary), ), const SizedBox(height: 8), _pickerCard( diff --git a/tdesign-component/example/assets/code/picker.buildCustomSize.txt b/tdesign-component/example/assets/code/picker.buildCustomSize.txt index f94fe3c70..b4606f643 100644 --- a/tdesign-component/example/assets/code/picker.buildCustomSize.txt +++ b/tdesign-component/example/assets/code/picker.buildCustomSize.txt @@ -5,7 +5,8 @@ children: [ Text( '示例:height(300) + itemCount(7),每屏显示 7 项', - style: TextStyle(fontSize: 12, color: TTheme.of(context).textColorPlaceholder), + style: TextStyle( + fontSize: 12, color: TTheme.of(context).textColorPlaceholder), ), const SizedBox(height: 8), _pickerCard( diff --git a/tdesign-component/example/assets/code/picker.buildCustomSlot.txt b/tdesign-component/example/assets/code/picker.buildCustomSlot.txt index 4c7137928..d3f56da0e 100644 --- a/tdesign-component/example/assets/code/picker.buildCustomSlot.txt +++ b/tdesign-component/example/assets/code/picker.buildCustomSlot.txt @@ -17,8 +17,7 @@ titleWidget: Row( mainAxisSize: MainAxisSize.min, children: [ - Icon(TIcons.location, - size: 18, color: theme.brandNormalColor), + Icon(TIcons.location, size: 18, color: theme.brandNormalColor), const SizedBox(width: 4), Text('选择地区', style: TextStyle( @@ -29,7 +28,8 @@ ], ), cancel: Icon(TIcons.close, size: 22, color: theme.fontGyColor2), - confirm: Icon(TIcons.check, size: 22, color: theme.brandNormalColor), + confirm: + Icon(TIcons.check, size: 22, color: theme.brandNormalColor), ), ), ], diff --git a/tdesign-component/example/assets/code/picker.buildGlobalDisabled.txt b/tdesign-component/example/assets/code/picker.buildGlobalDisabled.txt index f7abd42cf..bb1e7e3df 100644 --- a/tdesign-component/example/assets/code/picker.buildGlobalDisabled.txt +++ b/tdesign-component/example/assets/code/picker.buildGlobalDisabled.txt @@ -21,13 +21,16 @@ const SizedBox(height: 8), _pickerCard( context, - child: TPicker(items: cityItems, initialValue: const ['GZ'], + child: TPicker( + items: cityItems, + initialValue: const ['GZ'], onChange: (v) => debugPrint('选中: $v'), disabled: globalDisabled), ), const SizedBox(height: 4), Text('切换开关可控制整个选择器的禁用/启用状态', - style: TextStyle(fontSize: 12, color: TTheme.of(context).textColorPlaceholder)), + style: TextStyle( + fontSize: 12, color: TTheme.of(context).textColorPlaceholder)), ], ); } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/picker.buildItemDisabled.txt b/tdesign-component/example/assets/code/picker.buildItemDisabled.txt index f7bf58114..7e80c7c81 100644 --- a/tdesign-component/example/assets/code/picker.buildItemDisabled.txt +++ b/tdesign-component/example/assets/code/picker.buildItemDisabled.txt @@ -3,15 +3,20 @@ return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('选中: ${selectedItemDisabled.isEmpty ? "未选择" : selectedItemDisabled}', - style: TextStyle(fontSize: 14, color: TTheme.of(context).textColorSecondary)), + Text( + '选中: ${selectedItemDisabled.isEmpty ? "未选择" : selectedItemDisabled}', + style: TextStyle( + fontSize: 14, color: TTheme.of(context).textColorSecondary)), const SizedBox(height: 4), Text('提示: 标灰的选项不可选(第1列「保密」、第2列「A排1座/A排6座/A排7座/A排8座/A排12座」)', - style: TextStyle(fontSize: 12, color: TTheme.of(context).textColorPlaceholder)), + style: TextStyle( + fontSize: 12, color: TTheme.of(context).textColorPlaceholder)), const SizedBox(height: 8), _pickerCard( context, - child: TPicker(items: itemDisabledItems, initialValue: const ['M', 'A5'], + child: TPicker( + items: itemDisabledItems, + initialValue: const ['M', 'A5'], onChange: (v) => setState(() => selectedItemDisabled = '${v.labels.first} ${v.labels.last}')), ), diff --git a/tdesign-component/example/assets/code/picker.buildLinked.txt b/tdesign-component/example/assets/code/picker.buildLinked.txt index 90579f3c0..a3bfaea34 100644 --- a/tdesign-component/example/assets/code/picker.buildLinked.txt +++ b/tdesign-component/example/assets/code/picker.buildLinked.txt @@ -4,12 +4,16 @@ crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('选中地区: ${selectedLinked.isEmpty ? "未选择" : selectedLinked}', - style: TextStyle(fontSize: 14, color: TTheme.of(context).textColorSecondary)), + style: TextStyle( + fontSize: 14, color: TTheme.of(context).textColorSecondary)), const SizedBox(height: 8), _pickerCard( context, - child: TPicker(items: linkedItems, initialValue: const ['GD', 'SZ', 'NS'], - onChange: (v) => setState(() => selectedLinked = v.labels.join(' / '))), + child: TPicker( + items: linkedItems, + initialValue: const ['GD', 'SZ', 'NS'], + onChange: (v) => + setState(() => selectedLinked = v.labels.join(' / '))), ), ], ); diff --git a/tdesign-component/example/assets/code/picker.buildSingleColumn.txt b/tdesign-component/example/assets/code/picker.buildSingleColumn.txt index 839df74f9..953af8c2a 100644 --- a/tdesign-component/example/assets/code/picker.buildSingleColumn.txt +++ b/tdesign-component/example/assets/code/picker.buildSingleColumn.txt @@ -4,11 +4,13 @@ crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('选中城市: ${selectedCity.isEmpty ? "未选择" : selectedCity}', - style: TextStyle(fontSize: 14, color: TTheme.of(context).textColorSecondary)), + style: TextStyle( + fontSize: 14, color: TTheme.of(context).textColorSecondary)), const SizedBox(height: 8), _pickerCard( context, - child: TPicker(items: cityItems, + child: TPicker( + items: cityItems, onChange: (v) => setState(() => selectedCity = v.labels.first)), ), ], diff --git a/tdesign-component/example/assets/code/picker.buildTimeSelect.txt b/tdesign-component/example/assets/code/picker.buildTimeSelect.txt index eb85ddf1f..f9c5f818f 100644 --- a/tdesign-component/example/assets/code/picker.buildTimeSelect.txt +++ b/tdesign-component/example/assets/code/picker.buildTimeSelect.txt @@ -4,13 +4,16 @@ crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('选中时间: ${selectedTime.isEmpty ? "未选择" : selectedTime}', - style: TextStyle(fontSize: 14, color: TTheme.of(context).textColorSecondary)), + style: TextStyle( + fontSize: 14, color: TTheme.of(context).textColorSecondary)), const SizedBox(height: 8), _pickerCard( context, - child: TPicker(items: timeItems, itemCount: 5, - onChange: (v) => setState(() => - selectedTime = '${v.values[0]}:${v.values[1].toString().padLeft(2, '0')}:${v.values[2].toString().padLeft(2, '0')}')), + child: TPicker( + items: timeItems, + itemCount: 5, + onChange: (v) => setState(() => selectedTime = + '${v.values[0]}:${v.values[1].toString().padLeft(2, '0')}:${v.values[2].toString().padLeft(2, '0')}')), ), ], ); diff --git a/tdesign-component/example/assets/code/popup._buildApiDuration.txt b/tdesign-component/example/assets/code/popup._buildApiDuration.txt new file mode 100644 index 000000000..eeb7fc98c --- /dev/null +++ b/tdesign-component/example/assets/code/popup._buildApiDuration.txt @@ -0,0 +1,22 @@ + + Widget _buildApiDuration(BuildContext context) { + return TButton( + text: 'animationDuration: 600ms', + isBlock: true, + theme: TButtonTheme.primary, + type: TButtonType.outline, + size: TButtonSize.large, + onTap: () { + TPopup.show( + context, + options: TPopupOptions.bottom( + height: 240, + animationDuration: const Duration(milliseconds: 600), + child: Container( + height: 200, + color: TTheme.of(context).bgColorContainer, + )), + ); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/popup._buildApiInset.txt b/tdesign-component/example/assets/code/popup._buildApiInset.txt new file mode 100644 index 000000000..03f9ad0a0 --- /dev/null +++ b/tdesign-component/example/assets/code/popup._buildApiInset.txt @@ -0,0 +1,23 @@ + + Widget _buildApiInset(BuildContext context) { + return TButton( + text: 'bottom inset.left/right', + isBlock: true, + theme: TButtonTheme.primary, + type: TButtonType.outline, + size: TButtonSize.large, + onTap: () { + TPopup.show( + context, + options: TPopupOptions.bottom( + height: 320, + inset: const TPopupBottomInset(left: 16, right: 16), + titleWidget: TText('左右留白'), + child: Container( + height: 240, + color: TTheme.of(context).bgColorContainer, + )), + ); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/popup._buildApiOnOverlayClick.txt b/tdesign-component/example/assets/code/popup._buildApiOnOverlayClick.txt new file mode 100644 index 000000000..d24828ea7 --- /dev/null +++ b/tdesign-component/example/assets/code/popup._buildApiOnOverlayClick.txt @@ -0,0 +1,22 @@ + + Widget _buildApiOnOverlayClick(BuildContext context) { + return TButton( + text: 'onOverlayClick', + isBlock: true, + theme: TButtonTheme.primary, + type: TButtonType.outline, + size: TButtonSize.large, + onTap: () { + TPopup.show( + context, + options: TPopupOptions.bottom( + height: 260, + onOverlayClick: () => TToast.showText('点击蒙层', context: context), + child: Container( + height: 200, + color: TTheme.of(context).bgColorContainer, + )), + ); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/popup._buildApiShowOverlayFalse.txt b/tdesign-component/example/assets/code/popup._buildApiShowOverlayFalse.txt new file mode 100644 index 000000000..58776fda0 --- /dev/null +++ b/tdesign-component/example/assets/code/popup._buildApiShowOverlayFalse.txt @@ -0,0 +1,25 @@ + + Widget _buildApiShowOverlayFalse(BuildContext context) { + return TButton( + text: 'showOverlay: false(透明模态)', + isBlock: true, + theme: TButtonTheme.primary, + type: TButtonType.outline, + size: TButtonSize.large, + onTap: () { + TPopup.show( + context, + options: TPopupOptions.bottom( + height: 280, + showOverlay: false, + modal: true, + // 不显示可见蒙层,但仍阻断背景交互;须保留其它关闭入口。 + titleWidget: const TText('透明模态'), + child: Container( + height: 200, + color: TTheme.of(context).bgColorContainer, + )), + ); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/popup._buildNestedPopup.txt b/tdesign-component/example/assets/code/popup._buildNestedPopup.txt new file mode 100644 index 000000000..2ffd8f295 --- /dev/null +++ b/tdesign-component/example/assets/code/popup._buildNestedPopup.txt @@ -0,0 +1,64 @@ + + Widget _buildNestedPopup(BuildContext context) { + return TButton( + text: '内层再弹一层(嵌套叠加)', + isBlock: true, + theme: TButtonTheme.primary, + type: TButtonType.outline, + size: TButtonSize.large, + onTap: () { + TPopupHandle? outerHandle; + outerHandle = TPopup.show( + context, + options: TPopupOptions.bottom( + height: 360, + headerBuilder: null, + child: Builder( + builder: (innerContext) { + return Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + TText( + '外层:headerBuilder: null,仅 child', + textColor: TTheme.of(innerContext).textColorSecondary, + ), + const SizedBox(height: 16), + TButton( + text: '打开内层 Popup', + isBlock: true, + theme: TButtonTheme.primary, + size: TButtonSize.large, + onTap: () { + TPopup.show( + innerContext, + options: TPopupOptions.bottom( + height: 280, + titleWidget: const TText('内层标题'), + child: Container( + height: 160, + color: TTheme.of(innerContext) + .bgColorSecondaryContainer, + ), + ), + ); + }, + ), + const SizedBox(height: 12), + TButton( + text: '关闭外层', + isBlock: true, + type: TButtonType.outline, + size: TButtonSize.large, + onTap: () => outerHandle?.close(), + ), + ], + ), + ); + }, + )), + ); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/popup._buildPopFromBottom.txt b/tdesign-component/example/assets/code/popup._buildPopFromBottom.txt index 39ec6d336..d10c8b269 100644 --- a/tdesign-component/example/assets/code/popup._buildPopFromBottom.txt +++ b/tdesign-component/example/assets/code/popup._buildPopFromBottom.txt @@ -7,15 +7,15 @@ type: TButtonType.outline, size: TButtonSize.large, onTap: () { - Navigator.of(context).push( - TSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.bottom, - builder: (context) { - return Container( - color: TTheme.of(context).bgColorContainer, - height: 240, - ); - }), + TPopup.show( + context, + options: TPopupOptions.bottom( + height: 240, + headerBuilder: null, + child: Container( + color: TTheme.of(context).bgColorContainer, + height: 240, + )), ); }, ); diff --git a/tdesign-component/example/assets/code/popup._buildPopFromBottomWithClose.txt b/tdesign-component/example/assets/code/popup._buildPopFromBottomWithClose.txt deleted file mode 100644 index 35edc505c..000000000 --- a/tdesign-component/example/assets/code/popup._buildPopFromBottomWithClose.txt +++ /dev/null @@ -1,24 +0,0 @@ - - Widget _buildPopFromBottomWithClose(BuildContext context) { - return TButton( - text: '底部弹出层-带关闭', - isBlock: true, - theme: TButtonTheme.primary, - type: TButtonType.outline, - size: TButtonSize.large, - onTap: () { - Navigator.of(context).push( - TSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.bottom, - builder: (context) { - return TPopupBottomDisplayPanel( - closeClick: () { - Navigator.maybePop(context); - }, - child: Container(height: 200), - ); - }), - ); - }, - ); - } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/popup._buildPopFromBottomWithCloseAndLeftTitle.txt b/tdesign-component/example/assets/code/popup._buildPopFromBottomWithCloseAndLeftTitle.txt deleted file mode 100644 index bdc0fdf7c..000000000 --- a/tdesign-component/example/assets/code/popup._buildPopFromBottomWithCloseAndLeftTitle.txt +++ /dev/null @@ -1,26 +0,0 @@ - - Widget _buildPopFromBottomWithCloseAndLeftTitle(BuildContext context) { - return TButton( - text: '底部弹出层-带左边标题及关闭', - isBlock: true, - theme: TButtonTheme.primary, - type: TButtonType.outline, - size: TButtonSize.large, - onTap: () { - Navigator.of(context).push( - TSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.bottom, - builder: (context) { - return TPopupBottomDisplayPanel( - title: '标题文字', - titleLeft: true, - closeClick: () { - Navigator.maybePop(context); - }, - child: Container(height: 200), - ); - }), - ); - }, - ); - } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/popup._buildPopFromBottomWithCloseAndTitle.txt b/tdesign-component/example/assets/code/popup._buildPopFromBottomWithCloseAndTitle.txt index a8eba6f9c..83ddf96b2 100644 --- a/tdesign-component/example/assets/code/popup._buildPopFromBottomWithCloseAndTitle.txt +++ b/tdesign-component/example/assets/code/popup._buildPopFromBottomWithCloseAndTitle.txt @@ -7,18 +7,49 @@ type: TButtonType.outline, size: TButtonSize.large, onTap: () { - Navigator.of(context).push( - TSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.bottom, - builder: (context) { - return TPopupBottomDisplayPanel( - title: '标题文字', - closeClick: () { - Navigator.maybePop(context); - }, - child: Container(height: 200), - ); - }), + TPopup.show( + context, + options: TPopupOptions.bottom( + height: 280, + cancelBuilder: (_, close) => GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: close, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 8), + child: TText( + '关闭', + textColor: TTheme.of(context).textColorSecondary, + font: TTheme.of(context).fontBodyLarge, + ), + ), + ), + titleWidget: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(TIcons.info_circle, + color: TTheme.of(context).brandNormalColor, size: 18), + const SizedBox(width: 4), + TText( + '自定义标题', + textColor: TTheme.of(context).brandNormalColor, + font: TTheme.of(context).fontTitleMedium, + ), + ], + ), + confirmBuilder: (_, close) => GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: close, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 8), + child: TText( + '完成', + textColor: TTheme.of(context).brandNormalColor, + font: TTheme.of(context).fontTitleMedium, + fontWeight: FontWeight.w600, + ), + ), + ), + child: Container(height: 200)), ); }, ); diff --git a/tdesign-component/example/assets/code/popup._buildPopFromBottomWithOperation.txt b/tdesign-component/example/assets/code/popup._buildPopFromBottomWithOperation.txt deleted file mode 100644 index 9d37be37f..000000000 --- a/tdesign-component/example/assets/code/popup._buildPopFromBottomWithOperation.txt +++ /dev/null @@ -1,29 +0,0 @@ - - Widget _buildPopFromBottomWithOperation(BuildContext context) { - return TButton( - text: '底部弹出层-带操作', - isBlock: true, - theme: TButtonTheme.primary, - type: TButtonType.outline, - size: TButtonSize.large, - onTap: () { - Navigator.of(context).push(TSlidePopupRoute( - modalBarrierColor: TTheme.of(context).fontGyColor2, - slideTransitionFrom: SlideTransitionFrom.bottom, - builder: (context) { - return TPopupBottomConfirmPanel( - leftClick: () { - Navigator.maybePop(context); - }, - rightClick: () { - TToast.showText('确定', context: context); - Navigator.maybePop(context); - }, - child: Container( - height: 200, - ), - ); - })); - }, - ); - } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/popup._buildPopFromBottomWithOperationAndTitle.txt b/tdesign-component/example/assets/code/popup._buildPopFromBottomWithOperationAndTitle.txt index dee0cb931..88e0ea6f0 100644 --- a/tdesign-component/example/assets/code/popup._buildPopFromBottomWithOperationAndTitle.txt +++ b/tdesign-component/example/assets/code/popup._buildPopFromBottomWithOperationAndTitle.txt @@ -7,23 +7,12 @@ type: TButtonType.outline, size: TButtonSize.large, onTap: () { - Navigator.of(context).push( - TSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.bottom, - builder: (context) { - return TPopupBottomConfirmPanel( - title: '标题文字', - leftClick: () { - Navigator.maybePop(context); - }, - rightClick: () { - TToast.showText('确定', context: context); - Navigator.maybePop(context); - }, - child: Container(height: 200), - ); - }, - ), + TPopup.show( + context, + options: TPopupOptions.bottom( + height: 280, + titleWidget: TText('标题文字'), + child: Container(height: 200)), ); }, ); diff --git a/tdesign-component/example/assets/code/popup._buildPopFromBottomWithTitle.txt b/tdesign-component/example/assets/code/popup._buildPopFromBottomWithTitle.txt deleted file mode 100644 index e88aae7e1..000000000 --- a/tdesign-component/example/assets/code/popup._buildPopFromBottomWithTitle.txt +++ /dev/null @@ -1,26 +0,0 @@ - - Widget _buildPopFromBottomWithTitle(BuildContext context) { - return TButton( - text: '底部弹出层-仅标题', - isBlock: true, - theme: TButtonTheme.primary, - type: TButtonType.outline, - size: TButtonSize.large, - onTap: () { - Navigator.of(context).push( - TSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.bottom, - builder: (context) { - return TPopupBottomDisplayPanel( - title: '标题文字', - hideClose: true, - // closeClick: () { - // Navigator.maybePop(context); - // }, - child: Container(height: 200), - ); - }), - ); - }, - ); - } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/popup._buildPopFromCenter.txt b/tdesign-component/example/assets/code/popup._buildPopFromCenter.txt index 2262272ba..1d4511bca 100644 --- a/tdesign-component/example/assets/code/popup._buildPopFromCenter.txt +++ b/tdesign-component/example/assets/code/popup._buildPopFromCenter.txt @@ -7,20 +7,19 @@ type: TButtonType.outline, size: TButtonSize.large, onTap: () { - Navigator.of(context).push( - TSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.center, - builder: (context) { - return Container( - decoration: BoxDecoration( - color: TTheme.of(context).bgColorContainer, - borderRadius: - BorderRadius.circular(TTheme.of(context).radiusLarge), - ), - width: 240, - height: 240, - ); - }), + TPopup.show( + context, + options: TPopupOptions.center( + closeBuilder: null, + child: Container( + decoration: BoxDecoration( + color: TTheme.of(context).bgColorContainer, + borderRadius: + BorderRadius.circular(TTheme.of(context).radiusLarge), + ), + width: 240, + height: 240, + )), ); }, ); diff --git a/tdesign-component/example/assets/code/popup._buildPopFromCenterWithClose.txt b/tdesign-component/example/assets/code/popup._buildPopFromCenterWithClose.txt index 08f3aa831..376499c95 100644 --- a/tdesign-component/example/assets/code/popup._buildPopFromCenterWithClose.txt +++ b/tdesign-component/example/assets/code/popup._buildPopFromCenterWithClose.txt @@ -7,18 +7,21 @@ type: TButtonType.outline, size: TButtonSize.large, onTap: () { - Navigator.of(context).push( - TSlidePopupRoute( - isDismissible: false, - slideTransitionFrom: SlideTransitionFrom.center, - builder: (context) { - return TPopupCenterPanel( - closeClick: () { - Navigator.maybePop(context); - }, - child: const SizedBox(width: 240, height: 240), - ); - }), + TPopup.show( + context, + options: TPopupOptions.center( + closeOnOverlayClick: false, + width: 240, + height: 240, + closeBuilder: (_, close) => IconButton( + icon: Icon( + TIcons.close_circle, + color: TTheme.of(context).fontWhColor1, + size: 32, + ), + onPressed: close, + ), + child: const SizedBox(width: 240, height: 240)), ); }, ); diff --git a/tdesign-component/example/assets/code/popup._buildPopFromCenterWithUnderClose.txt b/tdesign-component/example/assets/code/popup._buildPopFromCenterWithUnderClose.txt index e22ac50fe..b7dec4b2f 100644 --- a/tdesign-component/example/assets/code/popup._buildPopFromCenterWithUnderClose.txt +++ b/tdesign-component/example/assets/code/popup._buildPopFromCenterWithUnderClose.txt @@ -1,25 +1,31 @@ Widget _buildPopFromCenterWithUnderClose(BuildContext context) { return TButton( - text: '居中弹出层-关闭在下方', + text: '居中弹出层-自定义下方按钮', isBlock: true, theme: TButtonTheme.primary, type: TButtonType.outline, size: TButtonSize.large, onTap: () { - Navigator.of(context).push( - TSlidePopupRoute( - isDismissible: false, - slideTransitionFrom: SlideTransitionFrom.center, - builder: (context) { - return TPopupCenterPanel( - closeUnderBottom: true, - closeClick: () { - Navigator.maybePop(context); - }, - child: const SizedBox(width: 240, height: 240), - ); - }), + TPopup.show( + context, + options: TPopupOptions.center( + closeOnOverlayClick: true, + width: 240, + height: 200, + closeBuilder: (_, close) => IconButton( + icon: Icon( + TIcons.poweroff, + color: TTheme.of(context).fontWhColor1, + size: 36, + ), + onPressed: close, + ), + child: Container( + width: 240, + height: 200, + color: TTheme.of(context).bgColorContainer, + )), ); }, ); diff --git a/tdesign-component/example/assets/code/popup._buildPopFromLeft.txt b/tdesign-component/example/assets/code/popup._buildPopFromLeft.txt index fba388fa5..91184529b 100644 --- a/tdesign-component/example/assets/code/popup._buildPopFromLeft.txt +++ b/tdesign-component/example/assets/code/popup._buildPopFromLeft.txt @@ -7,15 +7,13 @@ type: TButtonType.outline, size: TButtonSize.large, onTap: () { - Navigator.of(context).push( - TSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.left, - builder: (context) { - return Container( - color: TTheme.of(context).bgColorContainer, - width: 280, - ); - }), + TPopup.show( + context, + options: TPopupOptions.left( + width: 280, + child: Container( + color: TTheme.of(context).bgColorContainer, + )), ); }, ); diff --git a/tdesign-component/example/assets/code/popup._buildPopFromRight.txt b/tdesign-component/example/assets/code/popup._buildPopFromRight.txt index 650e05195..3d06a9c16 100644 --- a/tdesign-component/example/assets/code/popup._buildPopFromRight.txt +++ b/tdesign-component/example/assets/code/popup._buildPopFromRight.txt @@ -7,15 +7,13 @@ type: TButtonType.outline, size: TButtonSize.large, onTap: () { - Navigator.of(context).push( - TSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.right, - builder: (context) { - return Container( - color: TTheme.of(context).bgColorContainer, - width: 280, - ); - }), + TPopup.show( + context, + options: TPopupOptions.right( + width: 280, + child: Container( + color: TTheme.of(context).bgColorContainer, + )), ); }, ); diff --git a/tdesign-component/example/assets/code/popup._buildPopFromTop.txt b/tdesign-component/example/assets/code/popup._buildPopFromTop.txt index f0675a65a..112a0abf4 100644 --- a/tdesign-component/example/assets/code/popup._buildPopFromTop.txt +++ b/tdesign-component/example/assets/code/popup._buildPopFromTop.txt @@ -7,21 +7,16 @@ type: TButtonType.outline, size: TButtonSize.large, onTap: () { - Navigator.of(context).push( - TSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.top, - open: () { - print('open'); - }, - opened: () { - print('opened'); - }, - builder: (context) { - return Container( - color: TTheme.of(context).bgColorContainer, - height: 240, - ); - }), + TPopup.show( + context, + options: TPopupOptions.top( + height: 240, + onOpen: () => print('open'), + onOpened: () => print('opened'), + child: Container( + color: TTheme.of(context).bgColorContainer, + height: 240, + )), ); }, ); diff --git a/tdesign-component/example/lib/component_test/popup_test.dart b/tdesign-component/example/lib/component_test/popup_test.dart index 7aeb93e9d..b0f9bea3c 100644 --- a/tdesign-component/example/lib/component_test/popup_test.dart +++ b/tdesign-component/example/lib/component_test/popup_test.dart @@ -9,7 +9,7 @@ class ConfirmDialogTestApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( - title: 'confirmDialog 测试示例', + title: 'TPopup 测试示例', theme: ThemeData(primarySwatch: Colors.blue), home: const TestPage(), ); @@ -24,43 +24,29 @@ class TestPage extends StatefulWidget { } class _TestPageState extends State { - final TextEditingController _searchNameController = TextEditingController(); - final TextEditingController _searchRemarkController = TextEditingController(); - void _showProblemDialog() { - - Navigator.of(context).push( - TSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.bottom, - builder: (context) { - return TPopupBottomDisplayPanel( - title: 'title', - radius: 20, - backgroundColor: const Color(0xFFFAFFFC), - closeClick: () { - Navigator.maybePop(context); - }, - child: Container( - padding: const EdgeInsets.only(left: 20, right: 20, bottom: 33), - decoration: const BoxDecoration(color: Colors.white), - child: const Column( - children: [ - Center( - child: Text("立即拨打"), - ), - ], - ), + TPopup.show( + context, + options: TPopupOptions.bottom( + titleWidget: TText('title'), + radius: 20, + backgroundColor: const Color(0xFFFAFFFC), + child: Container( + padding: const EdgeInsets.only(left: 20, right: 20, bottom: 33), + decoration: const BoxDecoration(color: Colors.white), + child: const Column( + children: [ + Center(child: Text('立即拨打')), + ], ), - ); - }, - ), + )), ); } @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: const Text('TConfirmDialog测试')), + appBar: AppBar(title: const Text('TPopup测试')), body: Center( child: TButton( child: const Text('显示问题弹窗'), @@ -69,11 +55,4 @@ class _TestPageState extends State { ), ); } - - @override - void dispose() { - _searchNameController.dispose(); - _searchRemarkController.dispose(); - super.dispose(); - } -} \ No newline at end of file +} diff --git a/tdesign-component/example/lib/page/t_indexes_page.dart b/tdesign-component/example/lib/page/t_indexes_page.dart index caf9b55e9..d0b1f5cf2 100644 --- a/tdesign-component/example/lib/page/t_indexes_page.dart +++ b/tdesign-component/example/lib/page/t_indexes_page.dart @@ -157,28 +157,22 @@ Widget _buildSimple(BuildContext context) { theme: TButtonTheme.primary, type: TButtonType.outline, onTap: () { - Navigator.of(context).push( - TSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.right, - modalTop: renderBox?.size.height, - builder: (context) { - return TIndexes( + TPopup.show( + context, + options: TPopupOptions.right( + width: 280, + inset: TPopupRightInset(top: renderBox?.size.height ?? 0), + child: TIndexes( indexList: indexList, builderContent: (context, index) { final list = _list.firstWhere( (element) => element['index'] == index)['children'] as List; return TCellGroup( - cells: list - .map((e) => TCell( - title: e, - )) - .toList(), + cells: list.map((e) => TCell(title: e)).toList(), ); }, - ); - }, - ), + )), ); }, ); @@ -195,12 +189,12 @@ Widget _buildOther(BuildContext context) { theme: TButtonTheme.primary, type: TButtonType.outline, onTap: () { - Navigator.of(context).push( - TSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.right, - modalTop: renderBox?.size.height, - builder: (context) { - return TIndexes( + TPopup.show( + context, + options: TPopupOptions.right( + width: 280, + inset: TPopupRightInset(top: renderBox?.size.height ?? 0), + child: TIndexes( indexList: indexList, capsuleTheme: true, builderContent: (context, index) { @@ -208,16 +202,10 @@ Widget _buildOther(BuildContext context) { (element) => element['index'] == index)['children'] as List; return TCellGroup( - cells: list - .map((e) => TCell( - title: e, - )) - .toList(), + cells: list.map((e) => TCell(title: e)).toList(), ); }, - ); - }, - ), + )), ); }, ); @@ -234,16 +222,16 @@ Widget _buildCustomIndexes(BuildContext context) { theme: TButtonTheme.primary, type: TButtonType.outline, onTap: () { - Navigator.of(context).push( - TSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.right, - modalTop: renderBox?.size.height, - builder: (context) { - return TIndexes( + TPopup.show( + context, + options: TPopupOptions.right( + width: 280, + inset: TPopupRightInset(top: renderBox?.size.height ?? 0), + child: TIndexes( indexList: indexList, builderIndex: (context, index, isActive) { return TText( - '自定义 ${index}', + '自定义 $index', textColor: isActive ? TTheme.of(context).brandNormalColor : TTheme.of(context).textColorPrimary, @@ -254,16 +242,10 @@ Widget _buildCustomIndexes(BuildContext context) { (element) => element['index'] == index)['children'] as List; return TCellGroup( - cells: list - .map((e) => TCell( - title: e, - )) - .toList(), + cells: list.map((e) => TCell(title: e)).toList(), ); }, - ); - }, - ), + )), ); }, ); diff --git a/tdesign-component/example/lib/page/t_picker_page.dart b/tdesign-component/example/lib/page/t_picker_page.dart index 744b06aa0..1b548b6f5 100644 --- a/tdesign-component/example/lib/page/t_picker_page.dart +++ b/tdesign-component/example/lib/page/t_picker_page.dart @@ -33,13 +33,16 @@ class _TPickerPageState extends State { final timeItems = TPickerColumns([ [ - for (int i = 0; i < 24; i++) TPickerOption(label: '${i.toString().padLeft(2, '0')}时', value: i), + for (int i = 0; i < 24; i++) + TPickerOption(label: '${i.toString().padLeft(2, '0')}时', value: i), ], [ - for (int i = 0; i < 60; i++) TPickerOption(label: '${i.toString().padLeft(2, '0')}分', value: i), + for (int i = 0; i < 60; i++) + TPickerOption(label: '${i.toString().padLeft(2, '0')}分', value: i), ], [ - for (int i = 0; i < 60; i++) TPickerOption(label: '${i.toString().padLeft(2, '0')}秒', value: i), + for (int i = 0; i < 60; i++) + TPickerOption(label: '${i.toString().padLeft(2, '0')}秒', value: i), ], ]); @@ -101,18 +104,18 @@ class _TPickerPageState extends State { ], // 第2列:开头 + 中间 + 结尾 各 1 个禁用(稀疏分布,留足操作空间) [ - TPickerOption(label: 'A排1座', value: 'A1', disabled: true), // 开头禁用 + TPickerOption(label: 'A排1座', value: 'A1', disabled: true), // 开头禁用 TPickerOption(label: 'A排2座', value: 'A2'), TPickerOption(label: 'A排3座', value: 'A3'), TPickerOption(label: 'A排4座', value: 'A4'), TPickerOption(label: 'A排5座', value: 'A5'), TPickerOption(label: 'A排6座', value: 'A6', disabled: true), - TPickerOption(label: 'A排7座', value: 'A7', disabled: true), // 中间偏后禁用 - TPickerOption(label: 'A排8座', value: 'A8', disabled: true), // 新增禁用 + TPickerOption(label: 'A排7座', value: 'A7', disabled: true), // 中间偏后禁用 + TPickerOption(label: 'A排8座', value: 'A8', disabled: true), // 新增禁用 TPickerOption(label: 'A排9座', value: 'A9'), TPickerOption(label: 'A排10座', value: 'A10'), TPickerOption(label: 'A排11座', value: 'A11'), - TPickerOption(label: 'A排12座', value: 'A12', disabled: true), // 结尾禁用 + TPickerOption(label: 'A排12座', value: 'A12', disabled: true), // 结尾禁用 ], ]); @@ -157,7 +160,8 @@ class _TPickerPageState extends State { ]), ExampleModule(title: '禁用状态', children: [ ExampleItem(desc: '项级 disabled(部分选项不可选)', builder: buildItemDisabled), - ExampleItem(desc: '全局 disabled(整组不可操作)', builder: buildGlobalDisabled), + ExampleItem( + desc: '全局 disabled(整组不可操作)', builder: buildGlobalDisabled), ]), ExampleModule(title: '弹窗模式(TPopup)', children: [ ExampleItem(desc: '弹窗-联动选择(省市区)', builder: buildPopupLinked), @@ -167,11 +171,13 @@ class _TPickerPageState extends State { ExampleItem(desc: '自定义按钮(图标 / 文字)', builder: buildCustomSlot), ]), ExampleModule(title: '自定义字段映射(keys)', children: [ - ExampleItem(desc: '数据字段非 label/value 时,用 keys 映射', builder: buildCustomKeys), + ExampleItem( + desc: '数据字段非 label/value 时,用 keys 映射', builder: buildCustomKeys), ]), ExampleModule(title: '尺寸与样式', children: [ ExampleItem(desc: '自定义高度和每屏显示数量', builder: buildCustomSize), - ExampleItem(desc: '自定义子项渲染(itemBuilder)', builder: buildCustomItemBuilder), + ExampleItem( + desc: '自定义子项渲染(itemBuilder)', builder: buildCustomItemBuilder), ]), ], ); @@ -184,17 +190,18 @@ class _TPickerPageState extends State { /// TPicker 自带「取消 / 标题 / 确认」工具栏,业务方在 onCancel/onConfirm /// 中自行决定是否调用 Navigator.pop 关闭弹窗。 void _showPickerPopup(BuildContext context, {required Widget picker}) { - Navigator.of(context).push( - TSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.bottom, - builder: (ctx) => Material( - color: TTheme.of(ctx).bgColorContainer, - child: SafeArea( - top: false, - child: picker, - ), - ), - ), + TPopup.show( + context, + options: TPopupOptions.bottom( + cancelBuilder: null, + confirmBuilder: null, + child: Material( + color: TTheme.of(context).bgColorContainer, + child: SafeArea( + top: false, + child: picker, + ), + )), ); } @@ -219,11 +226,13 @@ class _TPickerPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('选中城市: ${selectedCity.isEmpty ? "未选择" : selectedCity}', - style: TextStyle(fontSize: 14, color: TTheme.of(context).textColorSecondary)), + style: TextStyle( + fontSize: 14, color: TTheme.of(context).textColorSecondary)), const SizedBox(height: 8), _pickerCard( context, - child: TPicker(items: cityItems, + child: TPicker( + items: cityItems, onChange: (v) => setState(() => selectedCity = v.labels.first)), ), ], @@ -236,13 +245,16 @@ class _TPickerPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('选中时间: ${selectedTime.isEmpty ? "未选择" : selectedTime}', - style: TextStyle(fontSize: 14, color: TTheme.of(context).textColorSecondary)), + style: TextStyle( + fontSize: 14, color: TTheme.of(context).textColorSecondary)), const SizedBox(height: 8), _pickerCard( context, - child: TPicker(items: timeItems, itemCount: 5, - onChange: (v) => setState(() => - selectedTime = '${v.values[0]}:${v.values[1].toString().padLeft(2, '0')}:${v.values[2].toString().padLeft(2, '0')}')), + child: TPicker( + items: timeItems, + itemCount: 5, + onChange: (v) => setState(() => selectedTime = + '${v.values[0]}:${v.values[1].toString().padLeft(2, '0')}:${v.values[2].toString().padLeft(2, '0')}')), ), ], ); @@ -254,12 +266,16 @@ class _TPickerPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('选中地区: ${selectedLinked.isEmpty ? "未选择" : selectedLinked}', - style: TextStyle(fontSize: 14, color: TTheme.of(context).textColorSecondary)), + style: TextStyle( + fontSize: 14, color: TTheme.of(context).textColorSecondary)), const SizedBox(height: 8), _pickerCard( context, - child: TPicker(items: linkedItems, initialValue: const ['GD', 'SZ', 'NS'], - onChange: (v) => setState(() => selectedLinked = v.labels.join(' / '))), + child: TPicker( + items: linkedItems, + initialValue: const ['GD', 'SZ', 'NS'], + onChange: (v) => + setState(() => selectedLinked = v.labels.join(' / '))), ), ], ); @@ -272,15 +288,20 @@ class _TPickerPageState extends State { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('选中: ${selectedItemDisabled.isEmpty ? "未选择" : selectedItemDisabled}', - style: TextStyle(fontSize: 14, color: TTheme.of(context).textColorSecondary)), + Text( + '选中: ${selectedItemDisabled.isEmpty ? "未选择" : selectedItemDisabled}', + style: TextStyle( + fontSize: 14, color: TTheme.of(context).textColorSecondary)), const SizedBox(height: 4), Text('提示: 标灰的选项不可选(第1列「保密」、第2列「A排1座/A排6座/A排7座/A排8座/A排12座」)', - style: TextStyle(fontSize: 12, color: TTheme.of(context).textColorPlaceholder)), + style: TextStyle( + fontSize: 12, color: TTheme.of(context).textColorPlaceholder)), const SizedBox(height: 8), _pickerCard( context, - child: TPicker(items: itemDisabledItems, initialValue: const ['M', 'A5'], + child: TPicker( + items: itemDisabledItems, + initialValue: const ['M', 'A5'], onChange: (v) => setState(() => selectedItemDisabled = '${v.labels.first} ${v.labels.last}')), ), @@ -311,13 +332,16 @@ class _TPickerPageState extends State { const SizedBox(height: 8), _pickerCard( context, - child: TPicker(items: cityItems, initialValue: const ['GZ'], + child: TPicker( + items: cityItems, + initialValue: const ['GZ'], onChange: (v) => debugPrint('选中: $v'), disabled: globalDisabled), ), const SizedBox(height: 4), Text('切换开关可控制整个选择器的禁用/启用状态', - style: TextStyle(fontSize: 12, color: TTheme.of(context).textColorPlaceholder)), + style: TextStyle( + fontSize: 12, color: TTheme.of(context).textColorPlaceholder)), ], ); } @@ -372,8 +396,7 @@ class _TPickerPageState extends State { titleWidget: Row( mainAxisSize: MainAxisSize.min, children: [ - Icon(TIcons.location, - size: 18, color: theme.brandNormalColor), + Icon(TIcons.location, size: 18, color: theme.brandNormalColor), const SizedBox(width: 4), Text('选择地区', style: TextStyle( @@ -384,7 +407,8 @@ class _TPickerPageState extends State { ], ), cancel: Icon(TIcons.close, size: 22, color: theme.fontGyColor2), - confirm: Icon(TIcons.check, size: 22, color: theme.brandNormalColor), + confirm: + Icon(TIcons.check, size: 22, color: theme.brandNormalColor), ), ), ], @@ -502,7 +526,7 @@ class _TPickerPageState extends State { {'code': 'BJ', 'city': '北京', 'readonly': false}, {'code': 'SH', 'city': '上海', 'readonly': false}, {'code': 'GZ', 'city': '广州', 'readonly': false}, - {'code': 'SZ', 'city': '深圳', 'readonly': true}, // 演示禁用映射 + {'code': 'SZ', 'city': '深圳', 'readonly': true}, // 演示禁用映射 {'code': 'CD', 'city': '成都', 'readonly': false}, {'code': 'HZ', 'city': '杭州', 'readonly': false}, ], @@ -515,19 +539,22 @@ class _TPickerPageState extends State { @Demo(group: 'picker') Widget buildCustomKeys(BuildContext context) { // 用 keys 告诉组件「city 映射为 label,code 是 value,readonly 是 disabled」 - const keys = TPickerKeys(label: 'city', value: 'code', disabled: 'readonly'); + const keys = + TPickerKeys(label: 'city', value: 'code', disabled: 'readonly'); final label = _customKeysValue?.labels.join() ?? ''; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '后端原始字段:city / code / readonly。通过 keys(label: "city") 映射为 label', - style: TextStyle(fontSize: 12, color: TTheme.of(context).textColorPlaceholder), + style: TextStyle( + fontSize: 12, color: TTheme.of(context).textColorPlaceholder), ), const SizedBox(height: 4), Text( '当前选中:${label.isEmpty ? "未选择" : label}', - style: TextStyle(fontSize: 14, color: TTheme.of(context).textColorSecondary), + style: TextStyle( + fontSize: 14, color: TTheme.of(context).textColorSecondary), ), const SizedBox(height: 8), _pickerCard( @@ -551,7 +578,8 @@ class _TPickerPageState extends State { children: [ Text( '示例:height(300) + itemCount(7),每屏显示 7 项', - style: TextStyle(fontSize: 12, color: TTheme.of(context).textColorPlaceholder), + style: TextStyle( + fontSize: 12, color: TTheme.of(context).textColorPlaceholder), ), const SizedBox(height: 8), _pickerCard( @@ -576,12 +604,14 @@ class _TPickerPageState extends State { children: [ Text( '示例:itemBuilder 自定义子项渲染,可添加图标、背景色等', - style: TextStyle(fontSize: 12, color: TTheme.of(context).textColorPlaceholder), + style: TextStyle( + fontSize: 12, color: TTheme.of(context).textColorPlaceholder), ), const SizedBox(height: 4), Text( '选中: ${_customItemBuilderValue.isEmpty ? "未选择" : _customItemBuilderValue}', - style: TextStyle(fontSize: 14, color: TTheme.of(context).textColorSecondary), + style: TextStyle( + fontSize: 14, color: TTheme.of(context).textColorSecondary), ), const SizedBox(height: 8), _pickerCard( @@ -607,15 +637,19 @@ class _TPickerPageState extends State { content, style: TextStyle( fontSize: 16, - fontWeight: selected ? FontWeight.w600 : FontWeight.normal, - color: selected ? theme.brandNormalColor : theme.fontGyColor1, + fontWeight: + selected ? FontWeight.w600 : FontWeight.normal, + color: selected + ? theme.brandNormalColor + : theme.fontGyColor1, ), ), ], ), ); }, - onChange: (v) => setState(() => _customItemBuilderValue = v.labels.first), + onChange: (v) => + setState(() => _customItemBuilderValue = v.labels.first), ), ), ], diff --git a/tdesign-component/example/lib/page/t_popup_page.dart b/tdesign-component/example/lib/page/t_popup_page.dart index 892855a8c..898800b37 100644 --- a/tdesign-component/example/lib/page/t_popup_page.dart +++ b/tdesign-component/example/lib/page/t_popup_page.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:tdesign_flutter/tdesign_flutter.dart'; + import '../annotation/demo.dart'; import '../base/example_widget.dart'; @@ -9,6 +10,44 @@ import '../base/example_widget.dart'; class TPopupPage extends StatelessWidget { const TPopupPage({super.key}); + static const double _headerHeight = 58; + + /// 底部标题 + 关闭(自定义 headerBuilder:标题居中 + 右侧关闭图标)。 + static TPopupHeaderBuilder _bottomTitleCloseHeader({ + String? title, + }) { + return (BuildContext ctx, VoidCallback close) { + final theme = TTheme.of(ctx); + final headerTitle = (title != null && title.isNotEmpty) + ? TText( + title, + textColor: theme.textColorPrimary, + font: theme.fontTitleLarge, + fontWeight: FontWeight.w700, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ) + : null; + return SizedBox( + height: _headerHeight, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Row( + children: [ + Expanded( + child: Center(child: headerTitle ?? const SizedBox.shrink()), + ), + IconButton( + icon: Icon(TIcons.close, color: theme.textColorSecondary), + onPressed: close, + ), + ], + ), + ), + ); + }; + } + @override Widget build(BuildContext context) { return ExamplePage( @@ -32,438 +71,242 @@ class TPopupPage extends StatelessWidget { title: '组件示例', children: [ ExampleItem(builder: _buildPopFromBottomWithOperationAndTitle), - ExampleItem(builder: _buildPopFromBottomWithOperation), ExampleItem(builder: _buildPopFromBottomWithCloseAndTitle), - ExampleItem(builder: _buildPopFromBottomWithCloseAndLeftTitle), - ExampleItem(builder: _buildPopFromBottomWithClose), - ExampleItem(builder: _buildPopFromBottomWithTitle), ExampleItem(builder: _buildPopFromCenterWithClose), ExampleItem(builder: _buildPopFromCenterWithUnderClose), + ExampleItem(builder: _buildNestedPopup), + ], + ), + ExampleModule( + title: '更多 API', + children: [ + ExampleItem(builder: _buildApiInset), + ExampleItem(builder: _buildApiShowOverlayFalse), + ExampleItem(builder: _buildApiOnOverlayClick), + ExampleItem(builder: _buildApiDuration), ], ), ], test: [ ExampleItem( - desc: '操作栏超长文本,指定颜色', - builder: (_) { - return TButton( - text: '底部弹出层-带标题及操作', - isBlock: true, - theme: TButtonTheme.primary, - type: TButtonType.outline, - size: TButtonSize.large, - onTap: () { - Navigator.of(context).push( - TSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.bottom, - builder: (context) { - return TPopupBottomConfirmPanel( - title: '标题文字标题文字标题文字标题文字标题文字标题文字标题文字标题文字', - leftText: '点这里确认!', - leftTextColor: TTheme.of(context).brandNormalColor, - leftClick: () { - TToast.showText('确认', context: context); - Navigator.maybePop(context); - }, - rightText: '关闭', - rightTextColor: TTheme.of(context).errorNormalColor, - rightClick: () => Navigator.maybePop(context), - child: Container(height: 200), - ); - }, - ), - ); - }, - ); - }), - ExampleItem( - desc: '带关闭超长文本', - builder: (_) { - return TButton( - text: '底部弹出层-带标题及操作', - isBlock: true, - theme: TButtonTheme.primary, - type: TButtonType.outline, - size: TButtonSize.large, - onTap: () { - Navigator.of(context).push( - TSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.bottom, - builder: (context) { - return TPopupBottomDisplayPanel( - title: '标题文字标题文字标题文字标题文字标题文字标题文字标题文字', - closeColor: TTheme.of(context).errorNormalColor, - closeClick: () => Navigator.maybePop(context), - child: Container(height: 200), - ); - }), - ); - }, - ); - }), - ExampleItem( - desc: '修改圆角', - builder: (_) { - return Column( - // spacing: 16, - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - TButton( - text: '底部弹出层-修改圆角', - isBlock: true, - theme: TButtonTheme.primary, - type: TButtonType.outline, - size: TButtonSize.large, - onTap: () { - Navigator.of(context).push( - TSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.bottom, - builder: (context) { - return TPopupBottomDisplayPanel( - title: '标题文字标题文字标题文字标题文字标题文字标题文字标题文字', - closeColor: - TTheme.of(context).errorNormalColor, - closeClick: () => Navigator.maybePop(context), - child: Container(height: 200), - radius: 6, - ); - }), - ); - }, - ), - const SizedBox(height: 16), - TButton( - text: '底部弹出层-修改圆角', - isBlock: true, - theme: TButtonTheme.primary, - type: TButtonType.outline, - size: TButtonSize.large, - onTap: () { - Navigator.of(context).push( - TSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.bottom, - builder: (context) { - return TPopupBottomConfirmPanel( - title: '标题文字标题文字标题文字标题文字标题文字标题文字标题文字', - leftText: '点这里确认!', - leftTextColor: - TTheme.of(context).brandNormalColor, - leftClick: () { - TToast.showText('确认', context: context); - Navigator.maybePop(context); - }, - rightText: '关闭', - rightTextColor: - TTheme.of(context).errorNormalColor, - rightClick: () => Navigator.maybePop(context), - child: Container(height: 200), - radius: 6, - ); - }), - ); - }, - ), - const SizedBox(height: 16), - TButton( - text: '居中弹出层-修改圆角', - isBlock: true, - theme: TButtonTheme.primary, - type: TButtonType.outline, - size: TButtonSize.large, - onTap: () { - Navigator.of(context).push( - TSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.center, - builder: (context) { - return TPopupCenterPanel( - closeColor: - TTheme.of(context).errorNormalColor, - closeClick: () { - Navigator.maybePop(context); - }, - child: const SizedBox(height: 240, width: 240), - radius: 6, - ); - }), - ); - }, - ), - const SizedBox(height: 16), - TButton( - text: '居中弹出层-底部关闭-修改圆角', - isBlock: true, - theme: TButtonTheme.primary, - type: TButtonType.outline, - size: TButtonSize.large, - onTap: () { - Navigator.of(context).push( - TSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.center, - builder: (context) { - return TPopupCenterPanel( - closeUnderBottom: true, - closeClick: () { - Navigator.maybePop(context); - }, - child: const SizedBox(height: 240, width: 240), - radius: 6, - ); - }), - ); - }, - ), - ], - ); - }), + desc: '操作栏超长文本,指定颜色', + builder: (_) { + return TButton( + text: '底部弹出层-带标题及操作', + isBlock: true, + theme: TButtonTheme.primary, + type: TButtonType.outline, + size: TButtonSize.large, + onTap: () { + TPopup.show( + context, + options: TPopupOptions.bottom( + height: 280, + titleWidget: TText('标题文字标题文字标题文字标题文字标题文字标题文字标题文字'), + cancelBuilder: (_, close) => GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: close, + child: Padding( + padding: + const EdgeInsets.symmetric(horizontal: 4, vertical: 8), + child: TText( + '点这里确认!', + textColor: TTheme.of(context).brandNormalColor, + font: TTheme.of(context).fontBodyLarge, + ), + ), + ), + confirmBuilder: (_, close) => GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: close, + child: Padding( + padding: + const EdgeInsets.symmetric(horizontal: 4, vertical: 8), + child: TText( + '关闭', + textColor: TTheme.of(context).errorNormalColor, + font: TTheme.of(context).fontBodyLarge, + ), + ), + ), + child: Container(height: 200)), + ); + }, + ); + }, + ), ExampleItem( - desc: '自定义位置', + desc: '带关闭超长文本', builder: (_) { return TButton( - text: '自定义位置', + text: '底部弹出层-带标题及关闭', isBlock: true, theme: TButtonTheme.primary, type: TButtonType.outline, size: TButtonSize.large, onTap: () { - var renderBox = - navBarkey.currentContext!.findRenderObject() as RenderBox; - Navigator.of(context).push( - TSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.right, - modalTop: renderBox.size.height, - builder: (context) { - return Container( - color: TTheme.of(context).bgColorContainer, - width: 280, - ); - }, - ), + TPopup.show( + context, + options: TPopupOptions.bottom( + height: 280, + headerBuilder: _bottomTitleCloseHeader( + title: '标题文字标题文字标题文字标题文字标题文字标题文字标题文字', + ), + child: Container(height: 200)), ); }, ); }, ), ExampleItem( - desc: '弹出层包含输入框且不会被键盘遮挡', - builder: (_) { - return Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.spaceAround, - // spacing: 16, - children: [ - TButton( - text: '底部弹出层-键盘弹出默认遮挡', - isBlock: true, - theme: TButtonTheme.primary, - type: TButtonType.outline, - size: TButtonSize.large, - onTap: () { - Navigator.of(context).push( - TSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.bottom, - builder: (context) { - return TPopupBottomDisplayPanel( - title: '标题文字标题文字标题文字标题文字标题文字标题文字标题文字', - closeColor: - TTheme.of(context).errorNormalColor, - closeClick: () => Navigator.maybePop(context), - child: Material( - child: SizedBox( - height: 100, - child: TInput( - type: TInputType.normal, - leftLabel: '标签文字', - hintText: '请输入文字', - maxLength: 10, - additionInfo: '最大输入10个字符', - ), + desc: '修改圆角', + builder: (_) { + return Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + TButton( + text: '底部弹出层-修改圆角', + isBlock: true, + theme: TButtonTheme.primary, + type: TButtonType.outline, + size: TButtonSize.large, + onTap: () { + TPopup.show( + context, + options: TPopupOptions.bottom( + height: 280, + radius: 6, + headerBuilder: _bottomTitleCloseHeader( + title: '标题文字标题文字标题文字标题文字标题文字标题文字标题文字', + ), + child: Container(height: 200)), + ); + }, + ), + const SizedBox(height: 16), + TButton( + text: '底部弹出层-修改圆角', + isBlock: true, + theme: TButtonTheme.primary, + type: TButtonType.outline, + size: TButtonSize.large, + onTap: () { + TPopup.show( + context, + options: TPopupOptions.bottom( + height: 280, + radius: 6, + titleWidget: TText('标题文字标题文字标题文字标题文字标题文字标题文字标题文字'), + cancelBuilder: (_, close) => GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: close, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 4, vertical: 8), + child: TText( + '点这里确认!', + textColor: TTheme.of(context).brandNormalColor, + font: TTheme.of(context).fontBodyLarge, ), ), - radius: 6, - ); - }), - ); - }, - ), - const SizedBox(height: 16), - TButton( - text: '底部弹出层-键盘弹出不遮挡', - isBlock: true, - theme: TButtonTheme.primary, - type: TButtonType.outline, - size: TButtonSize.large, - onTap: () { - Navigator.of(context).push( - TSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.bottom, - focusMove: true, - builder: (context) { - return TPopupBottomDisplayPanel( - title: '标题文字标题文字标题文字标题文字标题文字标题文字标题文字', - closeColor: - TTheme.of(context).errorNormalColor, - closeClick: () { - Navigator.maybePop(context); - }, - child: Material( - child: SizedBox( - height: 100, - child: TInput( - type: TInputType.normal, - leftLabel: '标签文字', - hintText: '请输入文字', - maxLength: 10, - additionInfo: '最大输入10个字符', - ), + ), + confirmBuilder: (_, close) => GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: close, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 4, vertical: 8), + child: TText( + '关闭', + textColor: TTheme.of(context).errorNormalColor, + font: TTheme.of(context).fontBodyLarge, ), ), - radius: 6, - ); - }), - ); - }, - ), - const SizedBox(height: 16), - TButton( - text: '居中弹出层-键盘弹出不遮挡', - isBlock: true, - theme: TButtonTheme.primary, - type: TButtonType.outline, - size: TButtonSize.large, - onTap: () { - Navigator.of(context).push( - TSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.center, - focusMove: true, - builder: (context) { - return TPopupCenterPanel( - closeColor: - TTheme.of(context).errorNormalColor, - closeClick: () { - Navigator.maybePop(context); - }, - child: SizedBox( - height: 348, - child: Column( - children: [ - TInput( - type: TInputType.normal, - leftLabel: '标签文字1', - hintText: '请输入文字1', - maxLength: 10, - ), - TInput( - type: TInputType.normal, - leftLabel: '标签文字2', - hintText: '请输入文字2', - maxLength: 10, - ), - TInput( - type: TInputType.normal, - leftLabel: '标签文字3', - hintText: '请输入文字3', - maxLength: 10, - ), - TInput( - type: TInputType.normal, - leftLabel: '标签文字4', - hintText: '请输入文字4', - maxLength: 10, - ), - TInput( - type: TInputType.normal, - leftLabel: '会被键盘遮挡的输入框1', - hintText: '会被键盘遮挡小部分', - maxLength: 10, - ), - TInput( - type: TInputType.normal, - leftLabel: '会被键盘遮挡的输入框2', - hintText: '会被键盘遮挡全遮挡', - maxLength: 10, - ) - ], - ), + ), + child: Container(height: 200)), + ); + }, + ), + const SizedBox(height: 16), + TButton( + text: '居中弹出层-修改圆角', + isBlock: true, + theme: TButtonTheme.primary, + type: TButtonType.outline, + size: TButtonSize.large, + onTap: () { + TPopup.show( + context, + options: TPopupOptions.center( + width: 240, + height: 240, + radius: 6, + closeBuilder: (_, close) => IconButton( + icon: Icon( + TIcons.close_circle, + color: TTheme.of(context).errorNormalColor, + size: 32, ), - radius: 6, - ); - }), - ); - }, - ) - ], - ); - }), + onPressed: close, + ), + child: const SizedBox(height: 240, width: 240)), + ); + }, + ), + const SizedBox(height: 16), + TButton( + text: '居中弹出层-底部关闭-修改圆角', + isBlock: true, + theme: TButtonTheme.primary, + type: TButtonType.outline, + size: TButtonSize.large, + onTap: () { + TPopup.show( + context, + options: TPopupOptions.center( + width: 240, + height: 240, + radius: 6, + child: const SizedBox(height: 240, width: 240)), + ); + }, + ), + ], + ); + }, + ), ExampleItem( - /// todo fix 动画闪烁 - desc: '可拖动全屏', + desc: '自定义位置', builder: (_) { - return Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.spaceAround, - // spacing: 16, - children: [ - TButton( - text: '可拖动全屏', - isBlock: true, - theme: TButtonTheme.primary, - type: TButtonType.outline, - size: TButtonSize.large, - onTap: () { - Navigator.of(context).push( - TSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.bottom, - builder: (context) { - return TPopupBottomDisplayPanel( - title: '标题文字', - draggable: true, - closeColor: - TTheme.of(context).errorNormalColor, - closeClick: () { - Navigator.maybePop(context); - }, - child: Container(height: 200), - ); - }), - ); - }, - ), - const SizedBox(height: 16), - TButton( - text: '可拖动全屏-带标题及操作', - isBlock: true, - theme: TButtonTheme.primary, - type: TButtonType.outline, - size: TButtonSize.large, - onTap: () { - Navigator.of(context).push( - TSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.bottom, - builder: (context) { - return TPopupBottomConfirmPanel( - title: '标题文字', - draggable: true, - leftClick: () { - Navigator.maybePop(context); - }, - rightClick: () { - TToast.showText('确定', context: context); - Navigator.maybePop(context); - }, - child: Container(height: 200), - ); - }), - ); - }, - ), - ]); + return TButton( + text: '自定义位置', + isBlock: true, + theme: TButtonTheme.primary, + type: TButtonType.outline, + size: TButtonSize.large, + onTap: () { + final renderBox = + navBarkey.currentContext!.findRenderObject() as RenderBox; + TPopup.show( + context, + options: TPopupOptions.right( + width: 280, + inset: TPopupRightInset(top: renderBox.size.height), + child: Container( + color: TTheme.of(context).bgColorContainer, + )), + ); + }, + ); }, ), ], ); } + // --- 01 组件类型(保持原 Demo 文案与交互)--- + @Demo(group: 'popup') Widget _buildPopFromTop(BuildContext context) { return TButton( @@ -473,21 +316,16 @@ class TPopupPage extends StatelessWidget { type: TButtonType.outline, size: TButtonSize.large, onTap: () { - Navigator.of(context).push( - TSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.top, - open: () { - print('open'); - }, - opened: () { - print('opened'); - }, - builder: (context) { - return Container( - color: TTheme.of(context).bgColorContainer, - height: 240, - ); - }), + TPopup.show( + context, + options: TPopupOptions.top( + height: 240, + onOpen: () => print('open'), + onOpened: () => print('opened'), + child: Container( + color: TTheme.of(context).bgColorContainer, + height: 240, + )), ); }, ); @@ -502,15 +340,13 @@ class TPopupPage extends StatelessWidget { type: TButtonType.outline, size: TButtonSize.large, onTap: () { - Navigator.of(context).push( - TSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.left, - builder: (context) { - return Container( - color: TTheme.of(context).bgColorContainer, - width: 280, - ); - }), + TPopup.show( + context, + options: TPopupOptions.left( + width: 280, + child: Container( + color: TTheme.of(context).bgColorContainer, + )), ); }, ); @@ -525,20 +361,19 @@ class TPopupPage extends StatelessWidget { type: TButtonType.outline, size: TButtonSize.large, onTap: () { - Navigator.of(context).push( - TSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.center, - builder: (context) { - return Container( - decoration: BoxDecoration( - color: TTheme.of(context).bgColorContainer, - borderRadius: - BorderRadius.circular(TTheme.of(context).radiusLarge), - ), - width: 240, - height: 240, - ); - }), + TPopup.show( + context, + options: TPopupOptions.center( + closeBuilder: null, + child: Container( + decoration: BoxDecoration( + color: TTheme.of(context).bgColorContainer, + borderRadius: + BorderRadius.circular(TTheme.of(context).radiusLarge), + ), + width: 240, + height: 240, + )), ); }, ); @@ -553,15 +388,15 @@ class TPopupPage extends StatelessWidget { type: TButtonType.outline, size: TButtonSize.large, onTap: () { - Navigator.of(context).push( - TSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.bottom, - builder: (context) { - return Container( - color: TTheme.of(context).bgColorContainer, - height: 240, - ); - }), + TPopup.show( + context, + options: TPopupOptions.bottom( + height: 240, + headerBuilder: null, + child: Container( + color: TTheme.of(context).bgColorContainer, + height: 240, + )), ); }, ); @@ -576,77 +411,102 @@ class TPopupPage extends StatelessWidget { type: TButtonType.outline, size: TButtonSize.large, onTap: () { - Navigator.of(context).push( - TSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.right, - builder: (context) { - return Container( - color: TTheme.of(context).bgColorContainer, - width: 280, - ); - }), + TPopup.show( + context, + options: TPopupOptions.right( + width: 280, + child: Container( + color: TTheme.of(context).bgColorContainer, + )), ); }, ); } + // --- 02 组件示例 --- + + /// 外层 Popup 的 child 内再 `TPopup.show(innerContext, options: …)`:用各自 [TPopupHandle] 关闭。 @Demo(group: 'popup') - Widget _buildPopFromBottomWithOperationAndTitle(BuildContext context) { + Widget _buildNestedPopup(BuildContext context) { return TButton( - text: '底部弹出层-带标题及操作', + text: '内层再弹一层(嵌套叠加)', isBlock: true, theme: TButtonTheme.primary, type: TButtonType.outline, size: TButtonSize.large, onTap: () { - Navigator.of(context).push( - TSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.bottom, - builder: (context) { - return TPopupBottomConfirmPanel( - title: '标题文字', - leftClick: () { - Navigator.maybePop(context); - }, - rightClick: () { - TToast.showText('确定', context: context); - Navigator.maybePop(context); + TPopupHandle? outerHandle; + outerHandle = TPopup.show( + context, + options: TPopupOptions.bottom( + height: 360, + headerBuilder: null, + child: Builder( + builder: (innerContext) { + return Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + TText( + '外层:headerBuilder: null,仅 child', + textColor: TTheme.of(innerContext).textColorSecondary, + ), + const SizedBox(height: 16), + TButton( + text: '打开内层 Popup', + isBlock: true, + theme: TButtonTheme.primary, + size: TButtonSize.large, + onTap: () { + TPopup.show( + innerContext, + options: TPopupOptions.bottom( + height: 280, + titleWidget: const TText('内层标题'), + child: Container( + height: 160, + color: TTheme.of(innerContext) + .bgColorSecondaryContainer, + ), + ), + ); + }, + ), + const SizedBox(height: 12), + TButton( + text: '关闭外层', + isBlock: true, + type: TButtonType.outline, + size: TButtonSize.large, + onTap: () => outerHandle?.close(), + ), + ], + ), + ); }, - child: Container(height: 200), - ); - }, - ), + )), ); }, ); } @Demo(group: 'popup') - Widget _buildPopFromBottomWithOperation(BuildContext context) { + Widget _buildPopFromBottomWithOperationAndTitle(BuildContext context) { return TButton( - text: '底部弹出层-带操作', + text: '底部弹出层-带标题及操作', isBlock: true, theme: TButtonTheme.primary, type: TButtonType.outline, size: TButtonSize.large, onTap: () { - Navigator.of(context).push(TSlidePopupRoute( - modalBarrierColor: TTheme.of(context).fontGyColor2, - slideTransitionFrom: SlideTransitionFrom.bottom, - builder: (context) { - return TPopupBottomConfirmPanel( - leftClick: () { - Navigator.maybePop(context); - }, - rightClick: () { - TToast.showText('确定', context: context); - Navigator.maybePop(context); - }, - child: Container( - height: 200, - ), - ); - })); + TPopup.show( + context, + options: TPopupOptions.bottom( + height: 280, + titleWidget: TText('标题文字'), + child: Container(height: 200)), + ); }, ); } @@ -660,150 +520,209 @@ class TPopupPage extends StatelessWidget { type: TButtonType.outline, size: TButtonSize.large, onTap: () { - Navigator.of(context).push( - TSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.bottom, - builder: (context) { - return TPopupBottomDisplayPanel( - title: '标题文字', - closeClick: () { - Navigator.maybePop(context); - }, - child: Container(height: 200), - ); - }), + TPopup.show( + context, + options: TPopupOptions.bottom( + height: 280, + cancelBuilder: (_, close) => GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: close, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 8), + child: TText( + '关闭', + textColor: TTheme.of(context).textColorSecondary, + font: TTheme.of(context).fontBodyLarge, + ), + ), + ), + titleWidget: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(TIcons.info_circle, + color: TTheme.of(context).brandNormalColor, size: 18), + const SizedBox(width: 4), + TText( + '自定义标题', + textColor: TTheme.of(context).brandNormalColor, + font: TTheme.of(context).fontTitleMedium, + ), + ], + ), + confirmBuilder: (_, close) => GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: close, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 8), + child: TText( + '完成', + textColor: TTheme.of(context).brandNormalColor, + font: TTheme.of(context).fontTitleMedium, + fontWeight: FontWeight.w600, + ), + ), + ), + child: Container(height: 200)), ); }, ); } @Demo(group: 'popup') - Widget _buildPopFromBottomWithCloseAndLeftTitle(BuildContext context) { + Widget _buildPopFromCenterWithClose(BuildContext context) { return TButton( - text: '底部弹出层-带左边标题及关闭', + text: '居中弹出层-带关闭', isBlock: true, theme: TButtonTheme.primary, type: TButtonType.outline, size: TButtonSize.large, onTap: () { - Navigator.of(context).push( - TSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.bottom, - builder: (context) { - return TPopupBottomDisplayPanel( - title: '标题文字', - titleLeft: true, - closeClick: () { - Navigator.maybePop(context); - }, - child: Container(height: 200), - ); - }), + TPopup.show( + context, + options: TPopupOptions.center( + closeOnOverlayClick: false, + width: 240, + height: 240, + closeBuilder: (_, close) => IconButton( + icon: Icon( + TIcons.close_circle, + color: TTheme.of(context).fontWhColor1, + size: 32, + ), + onPressed: close, + ), + child: const SizedBox(width: 240, height: 240)), ); }, ); } @Demo(group: 'popup') - Widget _buildPopFromBottomWithClose(BuildContext context) { + Widget _buildPopFromCenterWithUnderClose(BuildContext context) { return TButton( - text: '底部弹出层-带关闭', + text: '居中弹出层-自定义下方按钮', isBlock: true, theme: TButtonTheme.primary, type: TButtonType.outline, size: TButtonSize.large, onTap: () { - Navigator.of(context).push( - TSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.bottom, - builder: (context) { - return TPopupBottomDisplayPanel( - closeClick: () { - Navigator.maybePop(context); - }, - child: Container(height: 200), - ); - }), + TPopup.show( + context, + options: TPopupOptions.center( + closeOnOverlayClick: true, + width: 240, + height: 200, + closeBuilder: (_, close) => IconButton( + icon: Icon( + TIcons.poweroff, + color: TTheme.of(context).fontWhColor1, + size: 36, + ), + onPressed: close, + ), + child: Container( + width: 240, + height: 200, + color: TTheme.of(context).bgColorContainer, + )), ); }, ); } + // --- 更多 API --- + @Demo(group: 'popup') - Widget _buildPopFromBottomWithTitle(BuildContext context) { + Widget _buildApiInset(BuildContext context) { return TButton( - text: '底部弹出层-仅标题', + text: 'bottom inset.left/right', isBlock: true, theme: TButtonTheme.primary, type: TButtonType.outline, size: TButtonSize.large, onTap: () { - Navigator.of(context).push( - TSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.bottom, - builder: (context) { - return TPopupBottomDisplayPanel( - title: '标题文字', - hideClose: true, - // closeClick: () { - // Navigator.maybePop(context); - // }, - child: Container(height: 200), - ); - }), + TPopup.show( + context, + options: TPopupOptions.bottom( + height: 320, + inset: const TPopupBottomInset(left: 16, right: 16), + titleWidget: TText('左右留白'), + child: Container( + height: 240, + color: TTheme.of(context).bgColorContainer, + )), ); }, ); } @Demo(group: 'popup') - Widget _buildPopFromCenterWithClose(BuildContext context) { + Widget _buildApiShowOverlayFalse(BuildContext context) { return TButton( - text: '居中弹出层-带关闭', + text: 'showOverlay: false(透明模态)', isBlock: true, theme: TButtonTheme.primary, type: TButtonType.outline, size: TButtonSize.large, onTap: () { - Navigator.of(context).push( - TSlidePopupRoute( - isDismissible: false, - slideTransitionFrom: SlideTransitionFrom.center, - builder: (context) { - return TPopupCenterPanel( - closeClick: () { - Navigator.maybePop(context); - }, - child: const SizedBox(width: 240, height: 240), - ); - }), + TPopup.show( + context, + options: TPopupOptions.bottom( + height: 280, + showOverlay: false, + modal: true, + // 不显示可见蒙层,但仍阻断背景交互;须保留其它关闭入口。 + titleWidget: const TText('透明模态'), + child: Container( + height: 200, + color: TTheme.of(context).bgColorContainer, + )), ); }, ); } @Demo(group: 'popup') - Widget _buildPopFromCenterWithUnderClose(BuildContext context) { + Widget _buildApiOnOverlayClick(BuildContext context) { return TButton( - text: '居中弹出层-关闭在下方', + text: 'onOverlayClick', isBlock: true, theme: TButtonTheme.primary, type: TButtonType.outline, size: TButtonSize.large, onTap: () { - Navigator.of(context).push( - TSlidePopupRoute( - isDismissible: false, - slideTransitionFrom: SlideTransitionFrom.center, - builder: (context) { - return TPopupCenterPanel( - closeUnderBottom: true, - closeClick: () { - Navigator.maybePop(context); - }, - child: const SizedBox(width: 240, height: 240), - ); - }), + TPopup.show( + context, + options: TPopupOptions.bottom( + height: 260, + onOverlayClick: () => TToast.showText('点击蒙层', context: context), + child: Container( + height: 200, + color: TTheme.of(context).bgColorContainer, + )), + ); + }, + ); + } + + @Demo(group: 'popup') + Widget _buildApiDuration(BuildContext context) { + return TButton( + text: 'animationDuration: 600ms', + isBlock: true, + theme: TButtonTheme.primary, + type: TButtonType.outline, + size: TButtonSize.large, + onTap: () { + TPopup.show( + context, + options: TPopupOptions.bottom( + height: 240, + animationDuration: const Duration(milliseconds: 600), + child: Container( + height: 200, + color: TTheme.of(context).bgColorContainer, + )), ); }, ); diff --git a/tdesign-component/lib/src/components/action_sheet/t_action_sheet.dart b/tdesign-component/lib/src/components/action_sheet/t_action_sheet.dart index 9117f6a3d..8e51a6902 100644 --- a/tdesign-component/lib/src/components/action_sheet/t_action_sheet.dart +++ b/tdesign-component/lib/src/components/action_sheet/t_action_sheet.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import '../../util/context_extension.dart'; -import '../popup/t_popup_route.dart'; +import '../popup/t_popup.dart'; import 't_action_sheet.dart'; import 't_action_sheet_grid.dart'; import 't_action_sheet_group.dart'; @@ -9,7 +9,8 @@ import 't_action_sheet_list.dart'; export 't_action_sheet_item.dart'; -typedef TActionSheetItemCallback = void Function(TActionSheetItem item, int index); +typedef TActionSheetItemCallback = void Function( + TActionSheetItem item, int index); enum TActionSheetTheme { list, grid, group } @@ -112,7 +113,7 @@ class TActionSheet { /// 使用安全区域 final bool useSafeArea; - static TSlidePopupRoute? _actionSheetRoute; + static TPopupHandle? _actionSheetHandle; /// 显示列表类型面板 static void showListActionSheet( @@ -253,9 +254,7 @@ class TActionSheet { @mustCallSuper void close() { - if (_actionSheetRoute != null) { - Navigator.of(context).pop(); - } + _actionSheetHandle?.close(); } /// 创建路由 @@ -280,66 +279,70 @@ class TActionSheet { VoidCallback? onClose, bool useSafeArea = true, }) { - if (_actionSheetRoute != null) { + if (_actionSheetHandle?.isShowing == true) { return; } cancelText = cancelText ?? context.resource.cancel; - _actionSheetRoute = TSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.bottom, - isDismissible: showOverlay ? closeOnOverlayClick : false, - modalBarrierColor: showOverlay ? null : Colors.transparent, - builder: (context) { - switch (theme) { - case TActionSheetTheme.list: - return TActionSheetList( - items: items, - align: align, - cancelText: cancelText, - description: description, - showCancel: showCancel, - onCancel: onCancel, - onSelected: onSelected, - useSafeArea: useSafeArea, - ); - case TActionSheetTheme.grid: - return TActionSheetGrid( - items: items, - align: align, - onSelected: onSelected, - showCancel: showCancel, - showPagination: showPagination, - scrollable: scrollable, - cancelText: cancelText, - description: description, - count: count, - rows: rows, - onCancel: onCancel, - itemHeight: itemHeight, - itemMinWidth: itemMinWidth, - useSafeArea: useSafeArea, - ); - case TActionSheetTheme.group: - return TActionSheetGroup( - items: items, - align: align, - cancelText: cancelText, - showCancel: showCancel, - onCancel: onCancel, - onSelected: onSelected, - itemHeight: itemHeight, - itemMinWidth: itemMinWidth, - useSafeArea: useSafeArea, - ); - default: - return const SizedBox.shrink(); - } - }, + Widget sheetChild; + switch (theme) { + case TActionSheetTheme.list: + sheetChild = TActionSheetList( + items: items, + align: align, + cancelText: cancelText, + description: description, + showCancel: showCancel, + onCancel: onCancel, + onSelected: onSelected, + useSafeArea: useSafeArea, + ); + break; + case TActionSheetTheme.grid: + sheetChild = TActionSheetGrid( + items: items, + align: align, + onSelected: onSelected, + showCancel: showCancel, + showPagination: showPagination, + scrollable: scrollable, + cancelText: cancelText, + description: description, + count: count, + rows: rows, + onCancel: onCancel, + itemHeight: itemHeight, + itemMinWidth: itemMinWidth, + useSafeArea: useSafeArea, + ); + break; + case TActionSheetTheme.group: + sheetChild = TActionSheetGroup( + items: items, + align: align, + cancelText: cancelText, + showCancel: showCancel, + onCancel: onCancel, + onSelected: onSelected, + itemHeight: itemHeight, + itemMinWidth: itemMinWidth, + useSafeArea: useSafeArea, + ); + break; + } + + _actionSheetHandle = TPopup.show( + context, + options: TPopupOptions.bottom( + cancelBuilder: null, + confirmBuilder: null, + showOverlay: showOverlay, + closeOnOverlayClick: showOverlay && closeOnOverlayClick, + overlayColor: showOverlay ? null : Colors.transparent, + onClosed: onClose, + child: sheetChild, + ), ); - Navigator.of(context).push(_actionSheetRoute!).then((_) { - _actionSheetRoute = null; - onClose?.call(); - }); } } diff --git a/tdesign-component/lib/src/components/calendar/t_calendar_popup.dart b/tdesign-component/lib/src/components/calendar/t_calendar_popup.dart index b31fb034f..c812c3cee 100644 --- a/tdesign-component/lib/src/components/calendar/t_calendar_popup.dart +++ b/tdesign-component/lib/src/components/calendar/t_calendar_popup.dart @@ -53,7 +53,7 @@ class TCalendarPopup { /// 点击确认按钮时触发 final void Function(List value)? onConfirm; - static TSlidePopupRoute? _calendarPopup; + static TPopupHandle? _calendarHandle; /// 当前选中值 final ValueNotifier> _selected = ValueNotifier>([]); @@ -65,33 +65,43 @@ class TCalendarPopup { /// 打开日历 void show() { - if (_calendarPopup != null) { + if (_calendarHandle?.isShowing == true) { return; } - _calendarPopup = TSlidePopupRoute( - isDismissible: false, - slideTransitionFrom: SlideTransitionFrom.bottom, - modalTop: top, - barrierClick: () { - if (_autoClose) { - close(); - } - }, - builder: (context) { - final childWidget = builder?.call(context) ?? child; - return TCalendarInherited( + final childWidget = builder?.call(context) ?? child; + final topInset = top?.clamp(0.0, double.infinity).toDouble(); + final maxHeight = topInset == null + ? null + : (MediaQuery.sizeOf(context).height - topInset) + .clamp(0.0, double.infinity) + .toDouble(); + _calendarHandle = TPopup.show( + context, + options: TPopupOptions.bottom( + cancelBuilder: null, + confirmBuilder: null, + closeOnOverlayClick: false, + onOverlayClick: () { + if (_autoClose) { + close(); + } + }, + onClosed: _deleteRouter, + child: TCalendarInherited( selected: _selected, usePopup: true, confirmBtn: confirmBtn, onClose: _onClose, onConfirm: _onConfirm, - child: childWidget!, - ); - }, + child: maxHeight == null + ? childWidget! + : ConstrainedBox( + constraints: BoxConstraints(maxHeight: maxHeight), + child: childWidget!, + ), + ), + ), ); - Navigator.of(context).push(_calendarPopup!).then((_) { - _deleteRouter(); - }); } void _onClose() { @@ -109,14 +119,11 @@ class TCalendarPopup { /// 关闭日历 void close() { - if (_calendarPopup != null) { - Navigator.of(context).pop(); - // _deleteRouter(); - } + _calendarHandle?.close(); } void _deleteRouter() { - _calendarPopup = null; + _calendarHandle = null; onClose?.call(); } } diff --git a/tdesign-component/lib/src/components/drawer/t_drawer.dart b/tdesign-component/lib/src/components/drawer/t_drawer.dart index f4a4fea24..ac322e8f0 100644 --- a/tdesign-component/lib/src/components/drawer/t_drawer.dart +++ b/tdesign-component/lib/src/components/drawer/t_drawer.dart @@ -7,7 +7,7 @@ import '../cell/t_cell.dart'; import '../cell/t_cell_group.dart'; import '../cell/t_cell_style.dart'; import '../icon/t_icons.dart'; -import '../popup/t_popup_route.dart'; +import '../popup/t_popup.dart'; import 't_drawer_widget.dart'; /// 抽屉方向 @@ -98,25 +98,33 @@ class TDrawer { /// 是否显示最后一行分割线 final bool? isShowLastBordered; - TSlidePopupRoute? _drawerRoute; + TPopupHandle? _drawerHandle; void show() { - if (_drawerRoute != null) { - return; // 如果抽屉已经显示了,就不要再显示 + if (_drawerHandle?.isShowing == true) { + return; } final overlayEnabled = showOverlay ?? true; final dismissible = overlayEnabled && (closeOnOverlayClick ?? true); - - _drawerRoute = TSlidePopupRoute( - slideTransitionFrom: placement == TDrawerPlacement.right - ? SlideTransitionFrom.right - : SlideTransitionFrom.left, - isDismissible: dismissible, - modalBarrierColor: overlayEnabled ? null : Colors.transparent, - modalTop: drawerTop, - builder: (context) { - return TDrawerWidget( + final popupPlacement = placement == TDrawerPlacement.right + ? TPopupPlacement.right + : TPopupPlacement.left; + final popupInset = placement == TDrawerPlacement.right + ? TPopupRightInset(top: drawerTop ?? 0) + : TPopupLeftInset(top: drawerTop ?? 0); + + _drawerHandle = TPopup.show( + context, + options: TPopupOptions( + placement: popupPlacement, + width: width, + inset: popupInset, + showOverlay: overlayEnabled, + closeOnOverlayClick: dismissible, + overlayColor: overlayEnabled ? null : Colors.transparent, + onClosed: _deleteRouter, + child: TDrawerWidget( footer: footer, items: items, contentWidget: contentWidget, @@ -129,14 +137,9 @@ class TDrawer { backgroundColor: backgroundColor, bordered: bordered, isShowLastBordered: isShowLastBordered, - ); - }, + ), + ), ); - - Navigator.of(context).push(_drawerRoute!).then((_) { - // 当抽屉关闭时,将_drawerRoute置为null - _deleteRouter(); - }); } void open() { @@ -145,14 +148,11 @@ class TDrawer { @mustCallSuper void close() { - if (_drawerRoute != null) { - Navigator.of(context).pop(); - _deleteRouter(); - } + _drawerHandle?.close(); } void _deleteRouter() { - _drawerRoute = null; + _drawerHandle = null; onClose?.call(); } } diff --git a/tdesign-component/lib/src/components/image_viewer/t_image_viewer.dart b/tdesign-component/lib/src/components/image_viewer/t_image_viewer.dart index afb7b4906..d22b14063 100644 --- a/tdesign-component/lib/src/components/image_viewer/t_image_viewer.dart +++ b/tdesign-component/lib/src/components/image_viewer/t_image_viewer.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; import '../../theme/t_colors.dart'; import '../../theme/t_theme.dart'; -import '../popup/t_popup_route.dart'; import 't_image_viewer_widget.dart'; /// 图片预览工具 diff --git a/tdesign-component/lib/src/components/picker/t_picker.dart b/tdesign-component/lib/src/components/picker/t_picker.dart index ced374659..762891e5c 100644 --- a/tdesign-component/lib/src/components/picker/t_picker.dart +++ b/tdesign-component/lib/src/components/picker/t_picker.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import '../../../tdesign_flutter.dart'; import '../../util/context_extension.dart'; +import '../../util/t_toolbar_pressable.dart'; import 'no_wave_behavior.dart'; // =============== 文件级常量(魔法数字归一) =============== @@ -10,12 +11,6 @@ import 'no_wave_behavior.dart'; /// 工具栏高度(px) const double _kToolbarHeight = 48; -/// 按钮按压态动画时长 -const Duration _kPressAnimDuration = Duration(milliseconds: 100); - -/// 按钮按压态透明度 -const double _kPressedOpacity = 0.5; - /// disabled 项修正动画 - 距离 ≤ 2 时的时长 const int _kCorrectAnimShortMs = 200; @@ -245,8 +240,6 @@ class _TPickerState extends State { final _scrollBehavior = NoWaveBehavior(); /// 工具栏按钮按压态(参考 TCheckbox 的反馈方式) - bool _cancelPressed = false; - bool _confirmPressed = false; double get _itemHeight => widget.height / widget.itemCount; @@ -399,23 +392,31 @@ class _TPickerState extends State { padding: EdgeInsets.symmetric(horizontal: theme.spacer16), child: Row( children: [ - _buildToolbarButton( - theme: theme, - pressed: _cancelPressed, - onPressChange: (v) => setState(() => _cancelPressed = v), - onTap: () => widget.onCancel?.call(), - defaultColor: theme.fontGyColor2, + TToolbarPressable( + onTap: widget.onCancel, + mergeTextStyle: TextStyle( + fontSize: theme.fontTitleMedium?.size ?? _kDefaultFontSize, + color: theme.fontGyColor2, + ), + mergeIconTheme: IconThemeData( + color: theme.fontGyColor2, + size: _kDefaultIconSize, + ), child: cancelText, ), Expanded( child: Center(child: _buildTitle(theme)), ), - _buildToolbarButton( - theme: theme, - pressed: _confirmPressed, - onPressChange: (v) => setState(() => _confirmPressed = v), + TToolbarPressable( onTap: () => widget.onConfirm?.call(_buildValue()), - defaultColor: theme.brandNormalColor, + mergeTextStyle: TextStyle( + fontSize: theme.fontTitleMedium?.size ?? _kDefaultFontSize, + color: theme.brandNormalColor, + ), + mergeIconTheme: IconThemeData( + color: theme.brandNormalColor, + size: _kDefaultIconSize, + ), child: confirmText, ), ], @@ -441,48 +442,6 @@ class _TPickerState extends State { ); } - /// 带按压反馈的工具栏按钮 - /// - /// - 按下时 [AnimatedOpacity] 平滑过渡到 [_kPressedOpacity] - /// - [DefaultTextStyle] / [IconTheme] 为默认 [Text] / [Icon] 提供统一样式, - /// 用户传入的 Widget 若已指定样式,会优先采用自己的样式(merge 语义) - Widget _buildToolbarButton({ - required TThemeData theme, - required bool pressed, - required ValueChanged onPressChange, - required VoidCallback onTap, - required Color defaultColor, - required Widget child, - }) { - final styledChild = DefaultTextStyle.merge( - style: TextStyle( - fontSize: theme.fontTitleMedium?.size ?? _kDefaultFontSize, - color: defaultColor, - ), - child: IconTheme.merge( - data: IconThemeData(color: defaultColor, size: _kDefaultIconSize), - child: child, - ), - ); - - return GestureDetector( - behavior: HitTestBehavior.opaque, - onTapDown: (_) => onPressChange(true), - onTapUp: (_) => onPressChange(false), - onTapCancel: () => onPressChange(false), - onTap: onTap, - child: AnimatedOpacity( - duration: _kPressAnimDuration, - opacity: pressed ? _kPressedOpacity : 1.0, - child: Padding( - padding: EdgeInsets.symmetric( - horizontal: theme.spacer8, vertical: theme.spacer12), - child: styledChild, - ), - ), - ); - } - Widget _buildColumn(int colIndex) { final data = _columns[colIndex]; if (data.isEmpty) { diff --git a/tdesign-component/lib/src/components/popup/_popup_center_close.dart b/tdesign-component/lib/src/components/popup/_popup_center_close.dart new file mode 100644 index 000000000..778f1535e --- /dev/null +++ b/tdesign-component/lib/src/components/popup/_popup_center_close.dart @@ -0,0 +1,69 @@ +part of 't_popup.dart'; + +/// center 面板外下方关闭控件:默认图标与自定义关闭槽位都上报 [TPopupTrigger.close]。 +Widget buildPopupCenterCloseControl({ + required BuildContext context, + required TPopupOptions options, + required VoidCallback onCloseSlotTap, + required void Function(TPopupTrigger trigger) onCloseWithTrigger, +}) { + if (_isPopupDefaultClose(options.closeBuilder)) { + final theme = TTheme.of(context); + return IconButton( + tooltip: context.resource.close, + icon: Icon( + TIcons.close_circle, + color: theme.fontWhColor1, + size: 32, + ), + onPressed: () => onCloseWithTrigger(TPopupTrigger.close), + ); + } + return options.closeBuilder!(context, onCloseSlotTap); +} + +/// center 布局:内容面板 + 面板外下方关闭区。 +class PopupCenterUnderClose extends StatelessWidget { + const PopupCenterUnderClose({ + super.key, + required this.options, + required this.content, + required this.onCloseWithTrigger, + }); + + final TPopupOptions options; + final Widget content; + final void Function(TPopupTrigger trigger) onCloseWithTrigger; + + @override + Widget build(BuildContext context) { + var panel = content; + if (options.width != null || options.height != null) { + panel = SizedBox( + width: options.width, + height: options.height, + child: content, + ); + } + + void onCloseSlotTap() => onCloseWithTrigger(TPopupTrigger.close); + + final closeControl = buildPopupCenterCloseControl( + context: context, + options: options, + onCloseSlotTap: onCloseSlotTap, + onCloseWithTrigger: onCloseWithTrigger, + ); + + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const SizedBox(height: 40), + panel, + const SizedBox(height: 24), + closeControl, + ], + ); + } +} diff --git a/tdesign-component/lib/src/components/popup/_popup_header.dart b/tdesign-component/lib/src/components/popup/_popup_header.dart new file mode 100644 index 000000000..06ec0c0b0 --- /dev/null +++ b/tdesign-component/lib/src/components/popup/_popup_header.dart @@ -0,0 +1,177 @@ +part of 't_popup.dart'; + +/// bottom 头部渲染;行为由 [TPopupOptions.hasBuiltInHeader]、[TPopupOptions.useCustomHeader] 决定。 +class PopupHeader extends StatelessWidget { + const PopupHeader({ + super.key, + required this.options, + required this.onCloseWithTrigger, + }); + + final TPopupOptions options; + final void Function(TPopupTrigger trigger) onCloseWithTrigger; + + static const double headerHeight = 58; + + void _close() { + onCloseWithTrigger(TPopupTrigger.custom); + } + + @override + Widget build(BuildContext context) { + if (!options.hasBuiltInHeader) { + return const SizedBox.shrink(); + } + + if (options.useCustomHeader) { + return options.headerBuilder!(context, _close); + } + + return SizedBox( + height: headerHeight, + child: _DefaultHeader( + options: options, + onCloseWithTrigger: onCloseWithTrigger, + ), + ); + } +} + +class _DefaultHeader extends StatelessWidget { + const _DefaultHeader({ + required this.options, + required this.onCloseWithTrigger, + }); + + final TPopupOptions options; + + /// 给「内置 sentinel cancel/confirm 按钮」用的 close 入口, + /// 按钮自己传 [TPopupTrigger.cancel] / [TPopupTrigger.confirm]。 + final void Function(TPopupTrigger trigger) onCloseWithTrigger; + + @override + Widget build(BuildContext context) { + final theme = TTheme.of(context); + final showCancel = options.cancelBuilder != null; + final showConfirm = options.confirmBuilder != null; + + final title = options.titleWidget; + + return Row( + children: [ + if (showCancel) + Padding( + padding: EdgeInsets.only(left: theme.spacer8), + child: _wrapDefaultCancelSemantics( + context: context, + child: _buildCancel(context, theme), + ), + ) + else + SizedBox(width: theme.spacer16), + Expanded( + child: title == null + ? const SizedBox.shrink() + : Center(child: _titleWrap(context, theme, title)), + ), + if (showConfirm) + Padding( + padding: EdgeInsets.only(right: theme.spacer8), + child: _wrapDefaultConfirmSemantics( + context: context, + child: _buildConfirm(context, theme), + ), + ) + else + SizedBox(width: theme.spacer16), + ], + ); + } + + Widget _wrapDefaultCancelSemantics({ + required BuildContext context, + required Widget child, + }) { + if (!_isPopupDefaultCancel(options.cancelBuilder)) { + return child; + } + return Semantics( + button: true, + label: _cancelSemanticsLabel(context, options), + excludeSemantics: true, + child: child, + ); + } + + Widget _wrapDefaultConfirmSemantics({ + required BuildContext context, + required Widget child, + }) { + if (!_isPopupDefaultConfirm(options.confirmBuilder)) { + return child; + } + return Semantics( + button: true, + label: _confirmSemanticsLabel(context, options), + excludeSemantics: true, + child: child, + ); + } + + Widget _titleWrap(BuildContext context, TThemeData theme, Widget child) { + // 标题内容由用户插槽决定样式,这里只做布局约束。 + return DefaultTextStyle.merge( + style: TextStyle( + color: theme.textColorPrimary, + fontSize: theme.fontTitleLarge?.size, + fontWeight: FontWeight.w700, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + child: child, + ); + } + + Widget _buildCancel(BuildContext context, TThemeData theme) { + if (_isPopupDefaultCancel(options.cancelBuilder)) { + return TToolbarPressable( + onTap: () => onCloseWithTrigger(TPopupTrigger.cancel), + child: TText( + context.resource.cancel, + textColor: theme.textColorSecondary, + font: theme.fontBodyLarge, + ), + ); + } + return options.cancelBuilder!( + context, + () => onCloseWithTrigger(TPopupTrigger.cancel), + ); + } + + Widget _buildConfirm(BuildContext context, TThemeData theme) { + if (_isPopupDefaultConfirm(options.confirmBuilder)) { + return TToolbarPressable( + onTap: () => onCloseWithTrigger(TPopupTrigger.confirm), + child: TText( + context.resource.confirm, + textColor: theme.brandNormalColor, + font: theme.fontTitleMedium, + fontWeight: FontWeight.w600, + ), + ); + } + return options.confirmBuilder!( + context, + () => onCloseWithTrigger(TPopupTrigger.confirm), + ); + } +} + +String _cancelSemanticsLabel(BuildContext context, TPopupOptions options) { + return context.resource.cancel; +} + +String _confirmSemanticsLabel(BuildContext context, TPopupOptions options) { + return context.resource.confirm; +} diff --git a/tdesign-component/lib/src/components/popup/_popup_layout.dart b/tdesign-component/lib/src/components/popup/_popup_layout.dart new file mode 100644 index 000000000..d55207c02 --- /dev/null +++ b/tdesign-component/lib/src/components/popup/_popup_layout.dart @@ -0,0 +1,116 @@ +part of 't_popup.dart'; + +/// 按 [TPopupPlacement] 计算 [Positioned];center 仅居中,尺寸由 [PopupShell] 约束。 +class PopupLayout { + PopupLayout({ + required this.placement, + this.inset, + this.width, + this.height, + }); + + final TPopupPlacement placement; + final TPopupInset? inset; + final double? width; + final double? height; + + static const double defaultDrawerWidth = 280; + + Widget wrapPositioned({required Widget child}) { + switch (placement) { + case TPopupPlacement.top: + final inset = + this.inset is TPopupTopInset ? this.inset as TPopupTopInset : null; + return Positioned( + top: 0, + left: inset?.left ?? 0, + right: inset?.right ?? 0, + height: height, + child: child, + ); + case TPopupPlacement.bottom: + final inset = this.inset is TPopupBottomInset + ? this.inset as TPopupBottomInset + : null; + final bottomHeight = _bottomHeight(); + if (bottomHeight != null) { + return Positioned( + left: inset?.left ?? 0, + right: inset?.right ?? 0, + bottom: 0, + height: bottomHeight, + child: child, + ); + } + return Positioned( + left: inset?.left ?? 0, + right: inset?.right ?? 0, + bottom: 0, + child: child, + ); + case TPopupPlacement.left: + final inset = this.inset is TPopupLeftInset + ? this.inset as TPopupLeftInset + : null; + return Positioned( + top: inset?.top ?? 0, + bottom: inset?.bottom ?? 0, + left: 0, + width: width ?? defaultDrawerWidth, + child: child, + ); + case TPopupPlacement.right: + final inset = this.inset is TPopupRightInset + ? this.inset as TPopupRightInset + : null; + return Positioned( + top: inset?.top ?? 0, + bottom: inset?.bottom ?? 0, + right: 0, + width: width ?? defaultDrawerWidth, + child: child, + ); + case TPopupPlacement.center: + return Positioned.fill( + child: Center(child: child), + ); + } + } + + double? _bottomHeight() { + if (height != null) { + return height; + } + return null; + } + + Alignment get alignment { + switch (placement) { + case TPopupPlacement.top: + return Alignment.topCenter; + case TPopupPlacement.bottom: + return Alignment.bottomCenter; + case TPopupPlacement.left: + return Alignment.centerLeft; + case TPopupPlacement.right: + return Alignment.centerRight; + case TPopupPlacement.center: + return Alignment.center; + } + } + + Offset slideOffset(double t) { + switch (placement) { + case TPopupPlacement.top: + return Offset(0, t - 1); + case TPopupPlacement.bottom: + return Offset(0, 1 - t); + case TPopupPlacement.left: + return Offset(t - 1, 0); + case TPopupPlacement.right: + return Offset(1 - t, 0); + case TPopupPlacement.center: + return Offset.zero; + } + } +} diff --git a/tdesign-component/lib/src/components/popup/_popup_route.dart b/tdesign-component/lib/src/components/popup/_popup_route.dart new file mode 100644 index 000000000..1efb0b507 --- /dev/null +++ b/tdesign-component/lib/src/components/popup/_popup_route.dart @@ -0,0 +1,241 @@ +part of 't_popup.dart'; + +/// 库内 [PopupRoute];由 [TPopupHandle.open] push,勿在外部直接构造。 +class _PopupNavigatorRoute extends PopupRoute { + _PopupNavigatorRoute({ + required this.options, + required this.onCloseWithTrigger, + }) : _layout = PopupLayout( + placement: options.placement, + inset: options.inset, + width: options.width, + height: options.height, + ); + + final TPopupOptions options; + final void Function(TPopupTrigger trigger) onCloseWithTrigger; + + late PopupLayout _layout; + bool _animationListenerAttached = false; + bool _openedFired = false; + bool _closedFired = false; + bool _closeStartFired = false; + String? _barrierSemanticsLabel; + + _PopupBarrierMode get _barrierMode { + if (!options.modal) { + return _PopupBarrierMode.nonModal; + } + return options.showOverlay + ? _PopupBarrierMode.modalOverlay + : _PopupBarrierMode.modalTransparent; + } + + Color get _barrierColor { + if (!options.showOverlay) { + return Colors.transparent; + } + final base = options.overlayColor ?? Colors.black54; + if (options.overlayOpacity != null) { + final opacity = options.overlayOpacity!.clamp(0.0, 1.0); + return base.withValues(alpha: base.a * opacity); + } + return base; + } + + @override + Duration get transitionDuration => options.animationDuration; + + @override + Duration get reverseTransitionDuration => options.animationDuration; + + @override + bool get barrierDismissible => false; + + @override + String? get barrierLabel => + options.showOverlay ? _barrierSemanticsLabel : null; + + @override + Color? get barrierColor => null; + + /// 非 opaque,避免透明区域露出 Modal 默认底色。 + @override + bool get opaque => false; + + @override + bool get maintainState => !options.destroyOnClose; + + @override + Widget buildModalBarrier() { + if (_barrierMode == _PopupBarrierMode.nonModal) { + return const SizedBox.shrink(); + } + if (_barrierMode == _PopupBarrierMode.modalOverlay && + options.closeOnOverlayClick) { + return ModalBarrier( + color: Colors.transparent, + dismissible: true, + onDismiss: _handleOverlayTap, + semanticsLabel: _resolveBarrierSemanticsLabel(navigator!.context), + barrierSemanticsDismissible: true, + ); + } + return ModalBarrier( + color: Colors.transparent, + dismissible: false, + barrierSemanticsDismissible: false, + ); + } + + /// 关闭开始前统一入口:触发 [TPopupOptions.onClose]、[onVisibleChange](false, …)。 + void fireCloseStart(TPopupTrigger trigger) { + if (_closeStartFired) { + return; + } + _closeStartFired = true; + options.onVisibleChange?.call(false, trigger); + options.onClose?.call(); + } + + @override + Widget buildPage( + BuildContext context, + Animation animation, + Animation secondaryAnimation, + ) { + return const SizedBox.shrink(); + } + + @override + Widget buildTransitions( + BuildContext context, + Animation animation, + Animation secondaryAnimation, + Widget child, + ) { + final curved = CurvedAnimation( + parent: animation, + curve: Curves.decelerate, + reverseCurve: Curves.easeOut, + ); + + _layout = PopupLayout( + placement: options.placement, + inset: options.inset, + width: options.width, + height: options.height, + ); + + final t = curved.value; + final shell = PopupShell( + options: options, + onCloseWithTrigger: onCloseWithTrigger, + ); + + Widget popupContent; + if (options.placement == TPopupPlacement.center) { + popupContent = Transform.scale( + scale: t, + alignment: Alignment.center, + child: shell, + ); + } else { + popupContent = FractionalTranslation( + translation: _layout.slideOffset(t), + child: shell, + ); + } + + final positioned = _layout.wrapPositioned(child: popupContent); + + final barrier = _buildBarrier(context, t); + + return Stack( + fit: StackFit.expand, + children: [ + if (_barrierMode == _PopupBarrierMode.modalOverlay) barrier, + positioned, + ], + ); + } + + Widget _buildBarrier(BuildContext context, double t) { + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: _handleOverlayTap, + child: Container( + color: _barrierColor.withValues( + alpha: _barrierColor.a * t, + ), + ), + ); + } + + String _resolveBarrierSemanticsLabel(BuildContext context) { + return _barrierSemanticsLabel ??= + MaterialLocalizations.of(context).modalBarrierDismissLabel; + } + + void _handleOverlayTap() { + options.onOverlayClick?.call(); + if (options.closeOnOverlayClick) { + onCloseWithTrigger(TPopupTrigger.overlay); + } + } + + void _onAnimationStatus(AnimationStatus status) { + if (status == AnimationStatus.completed && !_openedFired) { + _openedFired = true; + options.onOpened?.call(); + } + if (status == AnimationStatus.dismissed && !_closedFired) { + _closedFired = true; + options.onClosed?.call(); + } + } + + void _attachAnimationListener() { + if (_animationListenerAttached) { + return; + } + final anim = animation; + if (anim == null) { + return; + } + _animationListenerAttached = true; + anim.addStatusListener(_onAnimationStatus); + if (anim.status == AnimationStatus.completed) { + _onAnimationStatus(AnimationStatus.completed); + } + } + + @override + TickerFuture didPush() { + options.onOpen?.call(); + options.onVisibleChange?.call(true, TPopupTrigger.api); + final future = super.didPush(); + future.whenComplete(_attachAnimationListener); + return future; + } + + @override + bool didPop(T? result) { + fireCloseStart(TPopupTrigger.systemBack); + return super.didPop(result); + } + + @override + void dispose() { + if (!_closedFired) { + _closedFired = true; + options.onClosed?.call(); + } + if (_animationListenerAttached) { + animation?.removeStatusListener(_onAnimationStatus); + } + super.dispose(); + } +} + +enum _PopupBarrierMode { modalOverlay, modalTransparent, nonModal } diff --git a/tdesign-component/lib/src/components/popup/_popup_shell.dart b/tdesign-component/lib/src/components/popup/_popup_shell.dart new file mode 100644 index 000000000..b4817d010 --- /dev/null +++ b/tdesign-component/lib/src/components/popup/_popup_shell.dart @@ -0,0 +1,106 @@ +part of 't_popup.dart'; + +/// 浮层内容外壳:圆角、[PopupHeader](仅 bottom)与主体内容; +/// center 由 [PopupCenterUnderClose] 接管面板外下方关闭区。 +class PopupShell extends StatelessWidget { + const PopupShell({ + super.key, + required this.options, + required this.onCloseWithTrigger, + }); + + final TPopupOptions options; + final void Function(TPopupTrigger trigger) onCloseWithTrigger; + + @override + Widget build(BuildContext context) { + final theme = TTheme.of(context); + final radius = options.radius ?? theme.radiusExtraLarge; + final backgroundColor = options.backgroundColor ?? theme.bgColorContainer; + final borderRadius = _borderRadius(options.placement, radius); + + if (options.placement == TPopupPlacement.center) { + return _buildCenter(context, radius, backgroundColor); + } + + return _buildEdge(context, borderRadius, backgroundColor); + } + + Widget _buildCenter(BuildContext context, double radius, Color background) { + final panel = Container( + decoration: BoxDecoration( + color: background, + borderRadius: BorderRadius.circular(radius), + ), + clipBehavior: Clip.antiAlias, + child: options.child, + ); + + if (options.closeBuilder != null) { + return PopupCenterUnderClose( + options: options, + content: panel, + onCloseWithTrigger: onCloseWithTrigger, + ); + } + return SizedBox( + width: options.width, + height: options.height, + child: panel, + ); + } + + Widget _buildEdge( + BuildContext context, + BorderRadius? borderRadius, + Color background, + ) { + final useExpanded = options.placement == TPopupPlacement.left || + options.placement == TPopupPlacement.right || + options.height != null; + + final body = options.placement == TPopupPlacement.bottom + ? Column( + mainAxisSize: useExpanded ? MainAxisSize.max : MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + PopupHeader( + options: options, + onCloseWithTrigger: onCloseWithTrigger, + ), + if (useExpanded) Expanded(child: options.child) else options.child, + ], + ) + : (useExpanded + ? Column( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [Expanded(child: options.child)], + ) + : options.child); + + return Container( + decoration: BoxDecoration( + color: background, + borderRadius: borderRadius, + ), + clipBehavior: Clip.antiAlias, + child: body, + ); + } + + BorderRadius? _borderRadius(TPopupPlacement placement, double radius) { + switch (placement) { + case TPopupPlacement.top: + return BorderRadius.vertical(bottom: Radius.circular(radius)); + case TPopupPlacement.bottom: + return BorderRadius.vertical(top: Radius.circular(radius)); + case TPopupPlacement.left: + return BorderRadius.horizontal(right: Radius.circular(radius)); + case TPopupPlacement.right: + return BorderRadius.horizontal(left: Radius.circular(radius)); + case TPopupPlacement.center: + return BorderRadius.circular(radius); + } + } +} diff --git a/tdesign-component/lib/src/components/popup/_popup_tracker.dart b/tdesign-component/lib/src/components/popup/_popup_tracker.dart new file mode 100644 index 000000000..2ed042d7c --- /dev/null +++ b/tdesign-component/lib/src/components/popup/_popup_tracker.dart @@ -0,0 +1,25 @@ +part of 't_popup.dart'; + +/// 库内:按 [Navigator] 记录 [TPopupHandle] 栈,用于嵌套与关闭清理。 +abstract class _PopupTracker { + static final Map> _stacks = {}; + + static void push(NavigatorState navigator, TPopupHandle handle) { + _stacks.putIfAbsent(navigator, () => []).add(handle); + } + + static void remove(NavigatorState navigator, TPopupHandle handle) { + _stacks[navigator]?.remove(handle); + if (_stacks[navigator]?.isEmpty ?? false) { + _stacks.remove(navigator); + } + } + + static TPopupHandle? top(NavigatorState navigator) { + final stack = _stacks[navigator]; + if (stack == null || stack.isEmpty) { + return null; + } + return stack.last; + } +} diff --git a/tdesign-component/lib/src/components/popup/t_popup.dart b/tdesign-component/lib/src/components/popup/t_popup.dart new file mode 100644 index 000000000..4d175352d --- /dev/null +++ b/tdesign-component/lib/src/components/popup/t_popup.dart @@ -0,0 +1,85 @@ +/// TDesign 弹出层(Popup)组件库。 +/// +/// 对外 API: +/// * [TPopup] — 命令式打开浮层 +/// * [TPopupOptions] — 配置(推荐命名工厂) +/// * [TPopupHandle] — 显隐控制 +/// * [TPopupPlacement]、[TPopupTrigger] — 方向与关闭来源 +/// * [TPopupHeaderBuilder]、[TPopupSlotBuilder]、[TPopupVisibleChangeCallback] — 构建器类型 +library; + +import 'package:flutter/material.dart'; + +import '../../theme/t_colors.dart'; +import '../../theme/t_fonts.dart'; +import '../../theme/t_radius.dart'; +import '../../theme/t_spacers.dart'; +import '../../theme/t_theme.dart'; +import '../../util/context_extension.dart'; +import '../../util/t_toolbar_pressable.dart'; +import '../icon/t_icons.dart'; +import '../text/t_text.dart'; + +part '_popup_center_close.dart'; +part '_popup_header.dart'; +part '_popup_layout.dart'; +part '_popup_route.dart'; +part '_popup_shell.dart'; +part '_popup_tracker.dart'; +part 't_popup_handle.dart'; +part 't_popup_inset.dart'; +part 't_popup_options.dart'; +part 't_popup_types.dart'; + +/// 弹出层入口:五向滑入 / 居中弹出,支持蒙层、bottom 操作区、center 面板外下方关闭区。 +/// +/// 通过 [show] 命令式打开;返回 [TPopupHandle] 用于关闭与再次打开。 +/// 多次调用 [show] 会继续压入新的浮层路由,可用于叠加展示。 +/// +/// **示例** +/// +/// ```dart +/// final handle = TPopup.show( +/// context, +/// options: TPopupOptions.bottom( +/// titleWidget: const Text('标题'), +/// child: MyPanel(), +/// ), +/// ); +/// handle.close(); +/// handle.open(); +/// ``` +/// +/// 配置项见 [TPopupOptions];方向见 [TPopupPlacement]。 +final class TPopup { + const TPopup._(); + + /// 打开浮层并压入独立 [PopupRoute]。 + /// + /// [context] 用于查找 [Navigator] 并展示浮层。 + /// + /// [options] 浮层配置;方向固定时推荐 [TPopupOptions.bottom] 等命名工厂。 + /// + /// 返回 [TPopupHandle],可用 [TPopupHandle.close]、[TPopupHandle.open]、 + /// [TPopupHandle.isShowing] 控制与查询。 + /// 重复调用会继续 push 新的浮层;若需互斥请在业务层管理。 + /// + /// [navigatorContext] 可选,指定承载浮层的 [Navigator] 的 context,默认 [context]。 + /// + /// [useRootNavigator] 为 true 时使用根 [Navigator](嵌套导航场景)。 + static TPopupHandle show( + BuildContext context, { + required TPopupOptions options, + BuildContext? navigatorContext, + bool useRootNavigator = false, + }) { + final navContext = navigatorContext ?? context; + final handle = TPopupHandle._( + options: options, + navigatorContext: navigatorContext, + useRootNavigator: useRootNavigator, + ); + handle.open(navContext); + return handle; + } +} diff --git a/tdesign-component/lib/src/components/popup/t_popup_handle.dart b/tdesign-component/lib/src/components/popup/t_popup_handle.dart new file mode 100644 index 000000000..f64a9bace --- /dev/null +++ b/tdesign-component/lib/src/components/popup/t_popup_handle.dart @@ -0,0 +1,173 @@ +part of 't_popup.dart'; + +/// [TPopup.show] 的返回值,用于控制同一份 [TPopupOptions] 的多次打开与关闭。 +/// +/// **示例** +/// +/// ```dart +/// final handle = TPopup.show( +/// context, +/// options: TPopupOptions.bottom(child: panel), +/// ); +/// handle.close(); +/// handle.open(); // 可省略 context,复用已缓存的 Navigator +/// ``` +class TPopupHandle { + TPopupHandle._({ + required this.options, + this.navigatorContext, + this.useRootNavigator = false, + }); + + /// 创建时传入的配置;每次 [open] 会按 [TPopupOptions.placement] 裁剪无效字段后使用。 + final TPopupOptions options; + + /// 与 [TPopup.show] 的 [navigatorContext] 相同。 + final BuildContext? navigatorContext; + + /// 与 [TPopup.show] 的 [useRootNavigator] 相同。 + final bool useRootNavigator; + + _PopupNavigatorRoute? _route; + NavigatorState? _lastNavigator; + bool _isClosed = false; + int _openEpoch = 0; + + /// 浮层是否仍在展示(路由在栈中且未进入关闭流程)。 + bool get isShowing => _route != null && !_isClosed; + + /// 打开或重新打开浮层。 + /// + /// [context] 可选。首次调用须能解析 [Navigator](传入 [context] 或依赖 + /// [navigatorContext]);后续可省略,优先复用缓存的 [NavigatorState]。 + /// + /// 已展示时调用无副作用。Navigator 已销毁且未提供新 [context] 时,debug 下 assert, + /// release 下静默返回。 + /// + /// 配置非法时会直接抛出 [FlutterError],debug / release 行为一致。 + void open([BuildContext? context]) { + if (isShowing) { + return; + } + final navigator = _resolveNavigator(context); + if (navigator == null) { + assert( + false, + 'TPopupHandle.open: cannot resolve Navigator. ' + 'Either pass a valid context or ensure the handle was created ' + 'with a still-mounted navigatorContext.', + ); + return; + } + + final validationError = options._validatePlacementParams(); + if (validationError != null) { + _throwPopupOptionsValidationError(validationError); + } + final normalized = options.normalized(); + final onClosed = normalized.onClosed; + final openEpoch = ++_openEpoch; + + _isClosed = false; + _lastNavigator = navigator; + + _PopupNavigatorRoute? route; + + void closeWithTrigger(TPopupTrigger trigger) { + final currentRoute = route; + if (!isShowing || currentRoute == null) { + return; + } + _closeRoute( + navigator: navigator, + route: currentRoute, + trigger: trigger, + ); + } + + route = _PopupNavigatorRoute( + options: normalized.copyWith( + onClosed: () { + if (_openEpoch == openEpoch) { + onClosed?.call(); + } + }, + ), + onCloseWithTrigger: closeWithTrigger, + ); + _route = route; + + _PopupTracker.push(navigator, this); + + navigator.push(route).whenComplete(() { + _PopupTracker.remove(navigator, this); + final completedRoute = route; + if (completedRoute != null) { + _detachRoute(completedRoute); + } + }); + } + + /// 关闭当前展示的浮层;[TPopupOptions.onVisibleChange] 的 [TPopupTrigger] 为 + /// [TPopupTrigger.api]。 + /// + /// 已关闭或未展示时调用无副作用。 + /// 嵌套浮层场景下会关闭当前 handle 对应的那一层,而不会误关栈顶其它浮层。 + void close() { + final route = _route; + final navigator = route?.navigator ?? _lastNavigator; + if (!isShowing || route == null || navigator == null) { + return; + } + _closeRoute( + navigator: navigator, + route: route, + trigger: TPopupTrigger.api, + ); + } + + NavigatorState? _resolveNavigator(BuildContext? context) { + final explicitNavigator = _navigatorFromContext(context); + if (explicitNavigator != null) { + return explicitNavigator; + } + final cached = _lastNavigator; + if (cached != null && cached.mounted) { + return cached; + } + return _navigatorFromContext(navigatorContext); + } + + NavigatorState? _navigatorFromContext(BuildContext? context) { + if (context == null || !context.mounted) { + return null; + } + return Navigator.maybeOf(context, rootNavigator: useRootNavigator); + } + + void _markClosing() { + _isClosed = true; + } + + void _closeRoute({ + required NavigatorState navigator, + required _PopupNavigatorRoute route, + required TPopupTrigger trigger, + }) { + _markClosing(); + route.fireCloseStart(trigger); + if (identical(_PopupTracker.top(navigator), this)) { + navigator.pop(); + return; + } + navigator.removeRoute(route); + } + + void _detachRoute(_PopupNavigatorRoute route) { + if (!identical(_route, route)) { + return; + } + _isClosed = true; + _route = null; + } +} diff --git a/tdesign-component/lib/src/components/popup/t_popup_inset.dart b/tdesign-component/lib/src/components/popup/t_popup_inset.dart new file mode 100644 index 000000000..388564acd --- /dev/null +++ b/tdesign-component/lib/src/components/popup/t_popup_inset.dart @@ -0,0 +1,50 @@ +part of 't_popup.dart'; + +/// Popup 在交叉轴方向的边缘留白基类。 +abstract class TPopupInset { + const TPopupInset(); +} + +/// bottom 方向的左右留白。 +class TPopupBottomInset extends TPopupInset { + const TPopupBottomInset({ + this.left = 0, + this.right = 0, + }); + + final double left; + final double right; +} + +/// top 方向的左右留白。 +class TPopupTopInset extends TPopupInset { + const TPopupTopInset({ + this.left = 0, + this.right = 0, + }); + + final double left; + final double right; +} + +/// left 方向的上下留白。 +class TPopupLeftInset extends TPopupInset { + const TPopupLeftInset({ + this.top = 0, + this.bottom = 0, + }); + + final double top; + final double bottom; +} + +/// right 方向的上下留白。 +class TPopupRightInset extends TPopupInset { + const TPopupRightInset({ + this.top = 0, + this.bottom = 0, + }); + + final double top; + final double bottom; +} diff --git a/tdesign-component/lib/src/components/popup/t_popup_options.dart b/tdesign-component/lib/src/components/popup/t_popup_options.dart new file mode 100644 index 000000000..c64623ef0 --- /dev/null +++ b/tdesign-component/lib/src/components/popup/t_popup_options.dart @@ -0,0 +1,656 @@ +part of 't_popup.dart'; + +/// 用于 [TPopupOptions.copyWith] 区分"不传"与"显式 null"。 +const Object _unset = Object(); + +Never _throwPopupOptionsValidationError(String error) { + throw FlutterError('TPopupOptions: $error'); +} + +/// [TPopup.show] 的配置对象。 +/// +/// ## 如何创建 +/// +/// | 场景 | 推荐用法 | +/// |------|----------| +/// | 弹出方向已知 | [TPopupOptions.bottom]、[TPopupOptions.center]、[TPopupOptions.top]、[TPopupOptions.left]、[TPopupOptions.right] | +/// | 方向由变量决定 | 默认构造并设置 [placement];传错字段会在 [TPopup.show] / [TPopupHandle.open] 时抛 [FlutterError] | +/// +/// 命名工厂只暴露当前方向生效的字段(例如 [TPopupOptions.bottom] 无 [width] 参数)。 +/// +/// ## 字段与 [TPopupPlacement] +/// +/// | [TPopupPlacement] | 头部 / 关闭区 | 尺寸 | +/// |-------------------|-------------|------| +/// | [TPopupPlacement.bottom] | [headerBuilder]、[titleWidget]、[cancelBuilder]、[confirmBuilder] | [height]、[inset] | +/// | [TPopupPlacement.center] | [closeBuilder] | [width]、[height] | +/// | [TPopupPlacement.top] | — | [height]、[inset] | +/// | [TPopupPlacement.left]、[TPopupPlacement.right] | — | [width]、[inset] | +/// +/// ## Builder 三态([headerBuilder]、[cancelBuilder]、[confirmBuilder]、[closeBuilder]) +/// +/// | 传参方式 | 效果 | +/// |----------|------| +/// | 省略(使用默认值) | 渲染内置 UI | +/// | 显式 `null` | 隐藏该区域 | +/// | 自定义 [TPopupHeaderBuilder] / [TPopupSlotBuilder] | 完全替换;需自行提供交互与语义,可调用 `close` 关闭浮层 | +/// +/// [titleWidget] 默认为 `null`,表示无标题内容。 +/// +/// 生命周期回调见 [onOpen]、[onOpened]、[onClose]、[onClosed]、[onVisibleChange]、[onOverlayClick]。 +class TPopupOptions { + /// 通用构造;[placement] 在运行时才能确定时使用。 + /// + /// 方向已知时请优先使用 [TPopupOptions.bottom] 等命名工厂。 + const TPopupOptions({ + required this.child, + this.placement = TPopupPlacement.bottom, + this.width, + this.height, + this.inset, + this.radius, + this.backgroundColor, + this.showOverlay = true, + bool? closeOnOverlayClick, + this.overlayColor, + this.overlayOpacity, + this.modal = true, + this.destroyOnClose = false, + this.animationDuration = const Duration(milliseconds: 240), + this.headerBuilder = _kPopupDefaultHeader, + this.titleWidget, + this.cancelBuilder = _kPopupDefaultCancel, + this.confirmBuilder = _kPopupDefaultConfirm, + this.closeBuilder = _kPopupDefaultClose, + this.onOpen, + this.onOpened, + this.onClose, + this.onClosed, + this.onVisibleChange, + this.onOverlayClick, + }) : _closeOnOverlayClick = closeOnOverlayClick; + + /// 创建 [TPopupPlacement.bottom] 配置。 + /// + /// 固定 [placement] 为 [TPopupPlacement.bottom];默认带内置头部。 + /// 蒙层、动画、生命周期等字段语义见同名成员文档。 + factory TPopupOptions.bottom({ + required Widget child, + double? height, + TPopupBottomInset? inset, + TPopupHeaderBuilder? headerBuilder = _kPopupDefaultHeader, + Widget? titleWidget, + TPopupSlotBuilder? cancelBuilder = _kPopupDefaultCancel, + TPopupSlotBuilder? confirmBuilder = _kPopupDefaultConfirm, + double? radius, + Color? backgroundColor, + bool showOverlay = true, + bool? closeOnOverlayClick, + Color? overlayColor, + double? overlayOpacity, + bool modal = true, + bool destroyOnClose = false, + Duration animationDuration = const Duration(milliseconds: 240), + VoidCallback? onOpen, + VoidCallback? onOpened, + VoidCallback? onClose, + VoidCallback? onClosed, + TPopupVisibleChangeCallback? onVisibleChange, + VoidCallback? onOverlayClick, + }) => + TPopupOptions( + child: child, + placement: TPopupPlacement.bottom, + height: height, + inset: inset, + headerBuilder: headerBuilder, + titleWidget: titleWidget, + cancelBuilder: cancelBuilder, + confirmBuilder: confirmBuilder, + radius: radius, + backgroundColor: backgroundColor, + showOverlay: showOverlay, + closeOnOverlayClick: closeOnOverlayClick, + overlayColor: overlayColor, + overlayOpacity: overlayOpacity, + modal: modal, + destroyOnClose: destroyOnClose, + animationDuration: animationDuration, + onOpen: onOpen, + onOpened: onOpened, + onClose: onClose, + onClosed: onClosed, + onVisibleChange: onVisibleChange, + onOverlayClick: onOverlayClick, + ); + + /// 创建 [TPopupPlacement.center] 配置。 + /// + /// 固定 [placement] 为 [TPopupPlacement.center];默认展示面板外下方圆形关闭按钮。 + factory TPopupOptions.center({ + required Widget child, + double? width, + double? height, + TPopupSlotBuilder? closeBuilder = _kPopupDefaultClose, + double? radius, + Color? backgroundColor, + bool showOverlay = true, + bool? closeOnOverlayClick, + Color? overlayColor, + double? overlayOpacity, + bool modal = true, + bool destroyOnClose = false, + Duration animationDuration = const Duration(milliseconds: 240), + VoidCallback? onOpen, + VoidCallback? onOpened, + VoidCallback? onClose, + VoidCallback? onClosed, + TPopupVisibleChangeCallback? onVisibleChange, + VoidCallback? onOverlayClick, + }) => + TPopupOptions( + child: child, + placement: TPopupPlacement.center, + width: width, + height: height, + closeBuilder: closeBuilder, + radius: radius, + backgroundColor: backgroundColor, + showOverlay: showOverlay, + closeOnOverlayClick: closeOnOverlayClick, + overlayColor: overlayColor, + overlayOpacity: overlayOpacity, + modal: modal, + destroyOnClose: destroyOnClose, + animationDuration: animationDuration, + onOpen: onOpen, + onOpened: onOpened, + onClose: onClose, + onClosed: onClosed, + onVisibleChange: onVisibleChange, + onOverlayClick: onOverlayClick, + ); + + /// 创建 [TPopupPlacement.top] 配置。 + /// + /// 固定 [placement] 为 [TPopupPlacement.top];无内置头部。 + factory TPopupOptions.top({ + required Widget child, + double? height, + TPopupTopInset? inset, + double? radius, + Color? backgroundColor, + bool showOverlay = true, + bool? closeOnOverlayClick, + Color? overlayColor, + double? overlayOpacity, + bool modal = true, + bool destroyOnClose = false, + Duration animationDuration = const Duration(milliseconds: 240), + VoidCallback? onOpen, + VoidCallback? onOpened, + VoidCallback? onClose, + VoidCallback? onClosed, + TPopupVisibleChangeCallback? onVisibleChange, + VoidCallback? onOverlayClick, + }) => + TPopupOptions( + child: child, + placement: TPopupPlacement.top, + height: height, + inset: inset, + radius: radius, + backgroundColor: backgroundColor, + showOverlay: showOverlay, + closeOnOverlayClick: closeOnOverlayClick, + overlayColor: overlayColor, + overlayOpacity: overlayOpacity, + modal: modal, + destroyOnClose: destroyOnClose, + animationDuration: animationDuration, + onOpen: onOpen, + onOpened: onOpened, + onClose: onClose, + onClosed: onClosed, + onVisibleChange: onVisibleChange, + onOverlayClick: onOverlayClick, + ); + + /// 创建 [TPopupPlacement.left] 配置。 + /// + /// 固定 [placement] 为 [TPopupPlacement.left];未传 [width] 时布局默认宽度 280。 + factory TPopupOptions.left({ + required Widget child, + double? width, + TPopupLeftInset? inset, + double? radius, + Color? backgroundColor, + bool showOverlay = true, + bool? closeOnOverlayClick, + Color? overlayColor, + double? overlayOpacity, + bool modal = true, + bool destroyOnClose = false, + Duration animationDuration = const Duration(milliseconds: 240), + VoidCallback? onOpen, + VoidCallback? onOpened, + VoidCallback? onClose, + VoidCallback? onClosed, + TPopupVisibleChangeCallback? onVisibleChange, + VoidCallback? onOverlayClick, + }) => + TPopupOptions( + child: child, + placement: TPopupPlacement.left, + width: width, + inset: inset, + radius: radius, + backgroundColor: backgroundColor, + showOverlay: showOverlay, + closeOnOverlayClick: closeOnOverlayClick, + overlayColor: overlayColor, + overlayOpacity: overlayOpacity, + modal: modal, + destroyOnClose: destroyOnClose, + animationDuration: animationDuration, + onOpen: onOpen, + onOpened: onOpened, + onClose: onClose, + onClosed: onClosed, + onVisibleChange: onVisibleChange, + onOverlayClick: onOverlayClick, + ); + + /// 创建 [TPopupPlacement.right] 配置。 + /// + /// 固定 [placement] 为 [TPopupPlacement.right];未传 [width] 时布局默认宽度 280。 + factory TPopupOptions.right({ + required Widget child, + double? width, + TPopupRightInset? inset, + double? radius, + Color? backgroundColor, + bool showOverlay = true, + bool? closeOnOverlayClick, + Color? overlayColor, + double? overlayOpacity, + bool modal = true, + bool destroyOnClose = false, + Duration animationDuration = const Duration(milliseconds: 240), + VoidCallback? onOpen, + VoidCallback? onOpened, + VoidCallback? onClose, + VoidCallback? onClosed, + TPopupVisibleChangeCallback? onVisibleChange, + VoidCallback? onOverlayClick, + }) => + TPopupOptions( + child: child, + placement: TPopupPlacement.right, + width: width, + inset: inset, + radius: radius, + backgroundColor: backgroundColor, + showOverlay: showOverlay, + closeOnOverlayClick: closeOnOverlayClick, + overlayColor: overlayColor, + overlayOpacity: overlayOpacity, + modal: modal, + destroyOnClose: destroyOnClose, + animationDuration: animationDuration, + onOpen: onOpen, + onOpened: onOpened, + onClose: onClose, + onClosed: onClosed, + onVisibleChange: onVisibleChange, + onOverlayClick: onOverlayClick, + ); + + /// 浮层主体内容(必填)。 + final Widget child; + + /// 出现位置,默认 [TPopupPlacement.bottom]。 + final TPopupPlacement placement; + + /// 宽度;[TPopupPlacement.left]、[TPopupPlacement.right]、[TPopupPlacement.center] 生效。 + final double? width; + + /// 高度;[TPopupPlacement.top]、[TPopupPlacement.bottom] 生效;[TPopupPlacement.center] 约束面板尺寸。 + final double? height; + + /// 交叉轴边缘留白;具体类型由 [placement] 决定。 + /// + /// * [TPopupPlacement.bottom] 使用 [TPopupBottomInset] + /// * [TPopupPlacement.top] 使用 [TPopupTopInset] + /// * [TPopupPlacement.left] 使用 [TPopupLeftInset] + /// * [TPopupPlacement.right] 使用 [TPopupRightInset] + /// * [TPopupPlacement.center] 不支持 + final TPopupInset? inset; + + /// 内容区圆角,默认主题大圆角。 + final double? radius; + + /// 内容区背景色,默认主题容器色。 + final Color? backgroundColor; + + /// 是否绘制半透明蒙层。 + /// + /// 当 [modal] 为 true 且此值为 false 时,为“透明模态弹层”。 + final bool showOverlay; + + final bool? _closeOnOverlayClick; + + /// 点击可见蒙层是否关闭。 + /// + /// 省略时默认跟随 [showOverlay]:显示蒙层时为 true,否则为 false。 + bool get closeOnOverlayClick => _closeOnOverlayClick ?? showOverlay; + + /// 蒙层颜色,默认 black54。 + final Color? overlayColor; + + /// 蒙层透明度系数(0–1),与 [overlayColor] 的 alpha 相乘后用于绘制。 + final double? overlayOpacity; + + /// 是否以模态方式展示;为 true 时阻断背景交互与底层语义/焦点。 + /// + /// 结合 [showOverlay] 可表达三种模式: + /// * `modal=true, showOverlay=true`:标准模态弹层 + /// * `modal=true, showOverlay=false`:透明模态弹层 + /// * `modal=false, showOverlay=false`:非模态浮层 + final bool modal; + + /// 为 true 时路由 `maintainState` 为 false,关闭后不保留路由内 State。 + final bool destroyOnClose; + + /// 打开/关闭动画时长。 + final Duration animationDuration; + + /// bottom 头部;仅 [TPopupPlacement.bottom] 生效。三态见类文档「Builder 三态」。 + /// + /// 自定义时忽略 [titleWidget]、[cancelBuilder]、[confirmBuilder]。 + final TPopupHeaderBuilder? headerBuilder; + + /// bottom 标题插槽;仅 [headerBuilder] 为内置默认时生效。`null` 表示无标题。 + final Widget? titleWidget; + + /// bottom 左侧操作槽;仅 [headerBuilder] 为内置默认时生效。 + /// + /// 内置默认为「取消」,点击触发 [TPopupTrigger.cancel]。 + final TPopupSlotBuilder? cancelBuilder; + + /// bottom 右侧操作槽;仅 [headerBuilder] 为内置默认时生效。 + /// + /// 内置默认为「确定」,点击触发 [TPopupTrigger.confirm]。 + final TPopupSlotBuilder? confirmBuilder; + + /// center 面板外下方关闭区;仅 [TPopupPlacement.center] 生效。三态见类文档「Builder 三态」。 + /// + /// 内置默认点击触发 [TPopupTrigger.close]。 + final TPopupSlotBuilder? closeBuilder; + + /// 路由 push 时(打开动画开始前)。 + final VoidCallback? onOpen; + + /// 打开动画结束。 + final VoidCallback? onOpened; + + /// 开始关闭(与 [onVisibleChange] 的 `visible: false` 同期)。 + final VoidCallback? onClose; + + /// 当前展示周期真正结束。 + /// + /// 大多数场景下会在关闭动画结束后触发;非栈顶路由被直接移除时不保证存在关闭动画。 + final VoidCallback? onClosed; + + /// 显隐变化;第二个参数为 [TPopupTrigger]。 + final TPopupVisibleChangeCallback? onVisibleChange; + + /// 蒙层点击;是否关闭取决于 [closeOnOverlayClick]。 + final VoidCallback? onOverlayClick; + + /// 返回配置副本。 + /// + /// 未传入的字段保持原值;对头部/关闭相关插槽显式传入 `null` 表示隐藏该区域。 + TPopupOptions copyWith({ + Widget? child, + TPopupPlacement? placement, + Object? width = _unset, + Object? height = _unset, + Object? inset = _unset, + Object? radius = _unset, + Object? backgroundColor = _unset, + bool? showOverlay, + Object? closeOnOverlayClick = _unset, + Object? overlayColor = _unset, + Object? overlayOpacity = _unset, + bool? modal, + bool? destroyOnClose, + Duration? animationDuration, + Object? headerBuilder = _unset, + Object? titleWidget = _unset, + Object? cancelBuilder = _unset, + Object? confirmBuilder = _unset, + Object? closeBuilder = _unset, + Object? onOpen = _unset, + Object? onOpened = _unset, + Object? onClose = _unset, + Object? onClosed = _unset, + Object? onVisibleChange = _unset, + Object? onOverlayClick = _unset, + }) { + return TPopupOptions( + child: child ?? this.child, + placement: placement ?? this.placement, + width: + identical(width, _unset) ? this.width : (width as num?)?.toDouble(), + height: identical(height, _unset) + ? this.height + : (height as num?)?.toDouble(), + inset: identical(inset, _unset) ? this.inset : inset as TPopupInset?, + radius: identical(radius, _unset) + ? this.radius + : (radius as num?)?.toDouble(), + backgroundColor: identical(backgroundColor, _unset) + ? this.backgroundColor + : backgroundColor as Color?, + showOverlay: showOverlay ?? this.showOverlay, + closeOnOverlayClick: identical(closeOnOverlayClick, _unset) + ? _closeOnOverlayClick + : closeOnOverlayClick as bool?, + overlayColor: identical(overlayColor, _unset) + ? this.overlayColor + : overlayColor as Color?, + overlayOpacity: identical(overlayOpacity, _unset) + ? this.overlayOpacity + : (overlayOpacity as num?)?.toDouble(), + modal: modal ?? this.modal, + destroyOnClose: destroyOnClose ?? this.destroyOnClose, + animationDuration: animationDuration ?? this.animationDuration, + headerBuilder: identical(headerBuilder, _unset) + ? this.headerBuilder + : headerBuilder as TPopupHeaderBuilder?, + titleWidget: identical(titleWidget, _unset) + ? this.titleWidget + : titleWidget as Widget?, + cancelBuilder: identical(cancelBuilder, _unset) + ? this.cancelBuilder + : cancelBuilder as TPopupSlotBuilder?, + confirmBuilder: identical(confirmBuilder, _unset) + ? this.confirmBuilder + : confirmBuilder as TPopupSlotBuilder?, + closeBuilder: identical(closeBuilder, _unset) + ? this.closeBuilder + : closeBuilder as TPopupSlotBuilder?, + onOpen: identical(onOpen, _unset) ? this.onOpen : onOpen as VoidCallback?, + onOpened: identical(onOpened, _unset) + ? this.onOpened + : onOpened as VoidCallback?, + onClose: + identical(onClose, _unset) ? this.onClose : onClose as VoidCallback?, + onClosed: identical(onClosed, _unset) + ? this.onClosed + : onClosed as VoidCallback?, + onVisibleChange: identical(onVisibleChange, _unset) + ? this.onVisibleChange + : onVisibleChange as TPopupVisibleChangeCallback?, + onOverlayClick: identical(onOverlayClick, _unset) + ? this.onOverlayClick + : onOverlayClick as VoidCallback?, + ); + } + + /// {@nodoc} + TPopupOptions normalized() { + final isBottom = placement == TPopupPlacement.bottom; + final isCenter = placement == TPopupPlacement.center; + + return TPopupOptions( + child: child, + placement: placement, + width: width, + height: height, + inset: inset, + radius: radius, + backgroundColor: backgroundColor, + showOverlay: showOverlay, + closeOnOverlayClick: _closeOnOverlayClick, + overlayColor: overlayColor, + overlayOpacity: overlayOpacity, + modal: modal, + destroyOnClose: destroyOnClose, + animationDuration: animationDuration, + headerBuilder: isBottom ? headerBuilder : null, + titleWidget: isBottom ? titleWidget : null, + cancelBuilder: isBottom ? cancelBuilder : null, + confirmBuilder: isBottom ? confirmBuilder : null, + closeBuilder: isCenter ? closeBuilder : null, + onOpen: onOpen, + onOpened: onOpened, + onClose: onClose, + onClosed: onClosed, + onVisibleChange: onVisibleChange, + onOverlayClick: onOverlayClick, + ); + } + + /// {@nodoc} + bool get usesDefaultHeader => _isPopupDefaultHeader(headerBuilder); + + /// {@nodoc} + bool get usesDefaultCancel => _isPopupDefaultCancel(cancelBuilder); + + /// {@nodoc} + bool get usesDefaultConfirm => _isPopupDefaultConfirm(confirmBuilder); + + /// {@nodoc} + bool get usesDefaultClose => _isPopupDefaultClose(closeBuilder); + + /// {@nodoc} + bool get useCustomHeader => + placement == TPopupPlacement.bottom && + headerBuilder != null && + !_isPopupDefaultHeader(headerBuilder); + + /// {@nodoc} + bool get useDefaultHeader => + placement == TPopupPlacement.bottom && + _isPopupDefaultHeader(headerBuilder); + + /// {@nodoc} + bool get showCancelSlot => + placement == TPopupPlacement.bottom && + useDefaultHeader && + cancelBuilder != null; + + /// {@nodoc} + bool get showConfirmSlot => + placement == TPopupPlacement.bottom && + useDefaultHeader && + confirmBuilder != null; + + /// {@nodoc} + bool get hasBuiltInHeader { + if (placement != TPopupPlacement.bottom || headerBuilder == null) { + return false; + } + if (useCustomHeader) { + return true; + } + return cancelBuilder != null || + confirmBuilder != null || + titleWidget != null; + } + + /// {@nodoc} + void assertPlacementParams() { + assert(() { + final err = _validatePlacementParams(); + if (err != null) { + _throwPopupOptionsValidationError(err); + } + return true; + }()); + } + + String? _validatePlacementParams() { + switch (placement) { + case TPopupPlacement.top: + if (width != null) { + return 'width is not valid for placement=top; use height + inset.'; + } + if (inset != null && inset is! TPopupTopInset) { + return 'inset must be TPopupTopInset for placement=top.'; + } + break; + case TPopupPlacement.bottom: + if (width != null) { + return 'width is not valid for placement=bottom; use height + inset.'; + } + if (inset != null && inset is! TPopupBottomInset) { + return 'inset must be TPopupBottomInset for placement=bottom.'; + } + break; + case TPopupPlacement.left: + if (height != null) { + return 'height is not valid for placement=left; use width + inset.'; + } + if (inset != null && inset is! TPopupLeftInset) { + return 'inset must be TPopupLeftInset for placement=left.'; + } + break; + case TPopupPlacement.right: + if (height != null) { + return 'height is not valid for placement=right; use width + inset.'; + } + if (inset != null && inset is! TPopupRightInset) { + return 'inset must be TPopupRightInset for placement=right.'; + } + break; + case TPopupPlacement.center: + if (inset != null) { + return 'inset is not valid for placement=center.'; + } + break; + } + final hasBottomHeaderCustom = !_isPopupDefaultHeader(headerBuilder) || + titleWidget != null || + !_isPopupDefaultCancel(cancelBuilder) || + !_isPopupDefaultConfirm(confirmBuilder); + if (placement != TPopupPlacement.bottom && hasBottomHeaderCustom) { + return 'header/titleWidget/cancel/confirmBuilder only apply to ' + 'placement=bottom (got placement=$placement).'; + } + if (placement != TPopupPlacement.center && + !_isPopupDefaultClose(closeBuilder)) { + return 'closeBuilder only applies to placement=center ' + '(got placement=$placement).'; + } + if (showOverlay && !modal) { + return 'showOverlay=true requires modal=true.'; + } + if (!showOverlay && _closeOnOverlayClick == true) { + return 'closeOnOverlayClick=true requires showOverlay=true.'; + } + return null; + } +} diff --git a/tdesign-component/lib/src/components/popup/t_popup_panel.dart b/tdesign-component/lib/src/components/popup/t_popup_panel.dart deleted file mode 100644 index 9a6f3fa85..000000000 --- a/tdesign-component/lib/src/components/popup/t_popup_panel.dart +++ /dev/null @@ -1,641 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/material.dart'; -import '../../../tdesign_flutter.dart'; -import '../../util/context_extension.dart'; - -typedef PopupClick = Function(); - -/// 弹窗基类 -abstract class TPopupBasePanel extends StatefulWidget { - const TPopupBasePanel({ - Key? key, - required this.child, - this.title, - this.titleColor, - this.backgroundColor, - this.radius, - this.draggable = false, - this.maxHeightRatio = 0.9, - this.minHeightRatio = 0.3, - }) : super(key: key); - - /// 子控件 - final Widget child; - - /// 标题 - final String? title; - - /// 标题颜色 - final Color? titleColor; - - /// 背景颜色 - final Color? backgroundColor; - - /// 圆角 - final double? radius; - - /// 边缘是否可拖动 - final bool draggable; - - /// 最大高度比例 - final double maxHeightRatio; - - /// 最小高度比例 - final double minHeightRatio; - - @override - State createState(); -} - -abstract class _TPopupBaseState extends State - with SingleTickerProviderStateMixin { - final GlobalKey _childKey = GlobalKey(); - static const _dragHandleHeight = 24.0; - static const _headerHeight = 58.0; - - late AnimationController _controller; - double _maxHeight = 0; - double _minHeight = 0; - double _currentHeight = 0; - double _lastChildHeight = 0; - double _lastScreenHeight = 0; - double? _lastMaxHeightRatio; - double? _lastMinHeightRatio; - bool? _lastDraggable; - bool _isFullscreen = false; - bool _isAnimating = false; - bool _isDragging = false; - - @override - void initState() { - super.initState(); - _controller = AnimationController( - duration: const Duration(milliseconds: 400), - vsync: this, - ); - if (widget.draggable) { - _controller.addListener(_updateHeight); - } - WidgetsBinding.instance.addPostFrameCallback((_) => _measureChildHeight()); - } - - /// 测量子组件高度并更新弹窗布局参数 - /// 1.获取子组件渲染尺寸 - /// 2.计算实际需要的基础高度 - /// 3.动态计算计算最大最小高度比例 - /// 4.更新动画控制器状态 - void _measureChildHeight() { - // 获取子组件渲染对象 - final context = _childKey.currentContext; - if (context == null) { - return; - } - final renderBox = context.findRenderObject() as RenderBox?; - if (renderBox == null || !renderBox.hasSize) { - return; - } - - final screenHeight = MediaQuery.of(context).size.height; - final childHeight = renderBox.size.height; - - // 如果高度和相关配置没有变化,则不重新计算 - if (_lastChildHeight == childHeight && - _lastScreenHeight == screenHeight && - _lastMaxHeightRatio == widget.maxHeightRatio && - _lastMinHeightRatio == widget.minHeightRatio && - _lastDraggable == widget.draggable) { - return; - } - _lastChildHeight = childHeight; - _lastScreenHeight = screenHeight; - _lastMaxHeightRatio = widget.maxHeightRatio; - _lastMinHeightRatio = widget.minHeightRatio; - _lastDraggable = widget.draggable; - - final headerHeight = widget.draggable ? _headerHeight : _headerHeight; - final baseHeight = _dragHandleHeight + headerHeight + childHeight; - - // 动态计算最大最小高度 - final maxHeightByRatio = screenHeight * widget.maxHeightRatio; - final minHeightByRatio = screenHeight * widget.minHeightRatio; - - // 内容高度和比例约束 - _maxHeight = min(baseHeight, maxHeightByRatio); - _minHeight = max(baseHeight * 0.5, minHeightByRatio); - if (_minHeight > _maxHeight) { - _minHeight = _maxHeight; - } - - // 初始化当前高度 - _currentHeight = baseHeight.clamp(_minHeight, _maxHeight); - // // 同步动画控制器 如果不赋值,会导致“键盘弹出默认遮挡”用例无法展示 - // final newValue = ((_currentHeight - _minHeight) / - // (_maxHeight - _minHeight).clamp(0.1, 1.0)) - // .clamp(0.0, 1.0); - // if ((_controller.value - newValue).abs() > 0.001) { - // _controller.value = newValue; - // } - // 同步动画控制器 - _controller.value = (_currentHeight - _minHeight) / - (_maxHeight - _minHeight).clamp(0.1, 1.0); - } - - void _updateHeight() => setState(() { - _currentHeight = - _minHeight + (_maxHeight - _minHeight) * _controller.value; - }); - - void _toggleFullscreen(bool fullscreen) { - if (_isAnimating || _isFullscreen == fullscreen) { - return; - } - - setState(() { - _isFullscreen = fullscreen; - _maxHeight = fullscreen - ? MediaQuery.of(context).size.height - : MediaQuery.of(context).size.height * widget.maxHeightRatio; - }); - - _controller.animateTo( - fullscreen ? 1.0 : 0.0, - duration: const Duration(milliseconds: 350), - curve: Curves.fastOutSlowIn, - ); - } - - void _animateTo(double height) { - if (_isAnimating) { - return; - } - _isAnimating = true; - - final value = (height - _minHeight) / (_maxHeight - _minHeight); - _controller - .animateTo( - value.clamp(0.0, 1.0), - duration: const Duration(milliseconds: 300), - curve: Curves.easeOutBack, - ) - .whenComplete(() => _isAnimating = false); - } - - Widget _buildDragHandle() { - if (!widget.draggable) { - return const SizedBox.shrink(); - } - - return GestureDetector( - behavior: HitTestBehavior.opaque, - onVerticalDragUpdate: _handleDragUpdate, - onVerticalDragEnd: _handleDragEnd, - onDoubleTap: () => _toggleFullscreen(!_isFullscreen), - child: Container( - height: _dragHandleHeight, - alignment: Alignment.center, - child: Container( - width: 48, - height: 4, - decoration: BoxDecoration( - color: TTheme.of(context).componentStrokeColor, - borderRadius: BorderRadius.circular(2), - ), - ), - ), - ); - } - - @override - Widget build(BuildContext context) { - WidgetsBinding.instance.addPostFrameCallback((_) { - // 每次 build 都测量子内容高度,确保内容变化时高度自适应 (拖动时不测量) - if (!_isDragging) { - _measureChildHeight(); - } - }); - - return AnimatedBuilder( - animation: _controller, - builder: (context, _) => RepaintBoundary( - child: Container( - height: _currentHeight, - decoration: BoxDecoration( - color: - widget.backgroundColor ?? TTheme.of(context).bgColorContainer, - borderRadius: _isFullscreen - ? null - : BorderRadius.vertical( - top: Radius.circular( - widget.radius ?? TTheme.of(context).radiusExtraLarge)), - ), - child: Column(children: [ - _buildDragHandle(), - buildHeader(context), - SizedBox( - child: _buildContent(), - ), - ]), - ), - ), - ); - } - - Widget _buildContent() => NotificationListener( - onNotification: (notification) { - if (notification is ScrollUpdateNotification) { - final metrics = notification.metrics; - if ((metrics.pixels <= 0 || - metrics.pixels >= metrics.maxScrollExtent) && - notification.dragDetails != null) { - _handleDragUpdate(notification.dragDetails!); - } - } - return false; - }, - child: Container( - key: _childKey, - child: widget.child, - ), - ); - - @protected - void _handleDragUpdate(DragUpdateDetails details); - - @protected - void _handleDragEnd(DragEndDetails details); - - @protected - Widget buildHeader(BuildContext context); - - void _baseHandleDragUpdate(DragUpdateDetails details) { - _isDragging = true; - if (_isAnimating || !widget.draggable) { - return; - } - - final newHeight = _currentHeight - details.primaryDelta! * 1.2; - _currentHeight = newHeight.clamp(_minHeight, _maxHeight); - _controller.value = - (_currentHeight - _minHeight) / (_maxHeight - _minHeight); - } - - void _baseHandleDragEnd(DragEndDetails details) { - final velocity = details.velocity.pixelsPerSecond.dy; - final predictedHeight = _currentHeight + velocity * 0.15; - - if (predictedHeight > _maxHeight * 0.7 || velocity < -800) { - _animateTo(_maxHeight); - } else if (predictedHeight < _minHeight * 1.3 || velocity > 800) { - _animateTo(_minHeight); - } - _isDragging = false; - } -} - -/// 右上角带关闭的底部浮层面板 -class TPopupBottomDisplayPanel extends TPopupBasePanel { - const TPopupBottomDisplayPanel({ - super.key, - required super.child, - super.title, - super.titleColor, - this.titleFontSize, - this.titleLeft = false, - this.hideClose = false, - this.closeColor, - this.closeSize, - this.closeClick, - super.backgroundColor, - super.radius, - super.draggable, - super.maxHeightRatio, - super.minHeightRatio, - }); - - /// 标题字体大小 - final double? titleFontSize; - - /// 标题是否靠左 - final bool titleLeft; - - /// 是否隐藏关闭按钮 - final bool hideClose; - - /// 关闭按钮颜色 - final Color? closeColor; - - /// 关闭按钮图标尺寸 - final double? closeSize; - - /// 关闭按钮点击回调 - final PopupClick? closeClick; - - @override - State createState() => _TPopupBottomDisplayPanelState(); -} - -class _TPopupBottomDisplayPanelState - extends _TPopupBaseState { - @override - Widget buildHeader(BuildContext context) { - Widget header = Container( - alignment: widget.titleLeft ? Alignment.centerLeft : Alignment.center, - padding: const EdgeInsets.symmetric(horizontal: 16), - child: TText( - widget.title ?? '', - textColor: widget.titleColor ?? TTheme.of(context).textColorPrimary, - font: TTheme.of(context).fontTitleLarge?.withSize( - widget.titleFontSize?.toInt() ?? - TTheme.of(context).fontTitleLarge!.size.toInt()), - fontWeight: FontWeight.w700, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ); - - if (!widget.hideClose) { - header = Stack( - alignment: Alignment.centerLeft, - children: [ - Padding( - padding: - EdgeInsets.only(right: 40, left: widget.titleLeft ? 0 : 40), - child: header, - ), - Positioned( - right: 0, - child: IconButton( - icon: Icon( - TIcons.close, - color: widget.closeColor, - size: widget.closeSize, - ), - onPressed: widget.closeClick, - ), - ), - ], - ); - } - - return SizedBox( - height: widget.draggable - ? _TPopupBaseState._headerHeight - - _TPopupBaseState._dragHandleHeight - : _TPopupBaseState._headerHeight, - child: header, - ); - } - - @override - void _handleDragUpdate(DragUpdateDetails details) { - super._baseHandleDragUpdate(details); - - final progress = (_currentHeight - _minHeight) / (_maxHeight - _minHeight); - if (progress > 0.85 && !_isFullscreen) { - _toggleFullscreen(true); - } else if (progress < 0.75 && _isFullscreen) { - _toggleFullscreen(false); - } - } - - @override - void _handleDragEnd(DragEndDetails details) => - super._baseHandleDragEnd(details); -} - -/// 带确认的底部浮层面板 -class TPopupBottomConfirmPanel extends TPopupBasePanel { - const TPopupBottomConfirmPanel({ - super.key, - required super.child, - super.title, - super.titleColor, - this.leftText, - this.leftTextColor, - this.leftClick, - this.rightText, - this.rightTextColor, - this.rightClick, - this.titleFontSize, - this.leftTextFontSize, - this.rightTextFontSize, - super.backgroundColor, - super.radius, - super.draggable, - super.maxHeightRatio, - super.minHeightRatio, - }); - - /// 标题字体大小 - final double? titleFontSize; - - /// 左边文本 - final String? leftText; - - /// 左边文本字体大小 - final double? leftTextFontSize; - - /// 左边文本颜色 - final Color? leftTextColor; - - /// 左边文本点击回调 - final PopupClick? leftClick; - - /// 右边文本 - final String? rightText; - - /// 右边文本字体大小 - final double? rightTextFontSize; - - /// 右边文本颜色 - final Color? rightTextColor; - - /// 右边文本点击回调 - final PopupClick? rightClick; - - @override - State createState() => _TPopupBottomConfirmPanelState(); -} - -class _TPopupBottomConfirmPanelState - extends _TPopupBaseState { - @override - Widget buildHeader(BuildContext context) { - return SizedBox( - height: widget.draggable - ? _TPopupBaseState._headerHeight - - _TPopupBaseState._dragHandleHeight - : _TPopupBaseState._headerHeight, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - _buildActionButton( - text: widget.leftText ?? context.resource.cancel, - color: - widget.leftTextColor ?? TTheme.of(context).textColorSecondary, - onTap: widget.leftClick, - left: true, - ), - Expanded( - child: Center( - child: TText( - widget.title ?? '', - textColor: - widget.titleColor ?? TTheme.of(context).textColorPrimary, - font: TTheme.of(context).fontTitleLarge?.withSize( - widget.titleFontSize?.toInt() ?? - TTheme.of(context).fontTitleLarge!.size.toInt()), - fontWeight: FontWeight.w700, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ), - ), - _buildActionButton( - text: widget.rightText ?? context.resource.confirm, - color: - widget.rightTextColor ?? TTheme.of(context).brandNormalColor, - onTap: widget.rightClick, - left: false, - ), - ], - ), - ); - } - - Widget _buildActionButton({ - required String text, - required Color color, - required VoidCallback? onTap, - required bool left, - }) => - GestureDetector( - onTap: onTap, - child: Padding( - padding: EdgeInsets.only( - left: left ? 16 : 0, - right: left ? 0 : 16, - ), - child: TText( - text, - textColor: color, - font: (left - ? TTheme.of(context).fontBodyLarge - : TTheme.of(context).fontTitleMedium) - ?.withSize(left - ? widget.leftTextFontSize?.toInt() ?? - TTheme.of(context).fontBodyLarge!.size.toInt() - : widget.rightTextFontSize?.toInt() ?? - TTheme.of(context).fontTitleMedium!.size.toInt()), - fontWeight: left ? FontWeight.w400 : FontWeight.w600, - ), - ), - ); - - @override - void _handleDragUpdate(DragUpdateDetails details) { - super._baseHandleDragUpdate(details); - - const threshold = 0.15; - final progress = (_currentHeight - _minHeight) / (_maxHeight - _minHeight); - if (progress > (1 - threshold) && !_isFullscreen) { - _toggleFullscreen(true); - } else if (progress < threshold && _isFullscreen) { - _toggleFullscreen(false); - } - } - - @override - void _handleDragEnd(DragEndDetails details) => - super._baseHandleDragEnd(details); -} - -/// 居中浮层面板 -class TPopupCenterPanel extends StatelessWidget { - const TPopupCenterPanel({ - super.key, - required this.child, - this.closeUnderBottom = false, - this.closeColor, - this.closeClick, - this.backgroundColor, - this.radius, - this.closeSize, - }); - - /// 子控件 - final Widget child; - - /// 关闭按钮是否在视图框下方 - final bool closeUnderBottom; - - /// 关闭按钮颜色 - final Color? closeColor; - - /// 关闭按钮图标尺寸 - final double? closeSize; - - /// 关闭按钮点击回调 - final PopupClick? closeClick; - - /// 背景颜色 - final Color? backgroundColor; - - /// 圆角 - final double? radius; - - @override - Widget build(BuildContext context) { - if (closeUnderBottom) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - const SizedBox(height: 40), - Container( - margin: const EdgeInsets.symmetric(vertical: 24), - decoration: BoxDecoration( - color: backgroundColor ?? TTheme.of(context).bgColorContainer, - borderRadius: BorderRadius.circular( - radius ?? TTheme.of(context).radiusExtraLarge), - ), - child: child, - ), - IconButton( - icon: Icon( - TIcons.close_circle, - color: closeColor ?? TTheme.of(context).fontWhColor1, - size: closeSize ?? 32, - ), - onPressed: closeClick, - ), - ], - ); - } - - return Container( - decoration: BoxDecoration( - color: backgroundColor ?? TTheme.of(context).bgColorContainer, - borderRadius: BorderRadius.circular( - radius ?? TTheme.of(context).radiusExtraLarge), - ), - child: Stack( - children: [ - child, - Positioned( - top: TTheme.of(context).spacer8, - right: TTheme.of(context).spacer8, - child: IconButton( - icon: Icon( - TIcons.close, - color: closeColor, - size: closeSize, - ), - onPressed: closeClick, - ), - ), - ], - ), - ); - } -} diff --git a/tdesign-component/lib/src/components/popup/t_popup_route.dart b/tdesign-component/lib/src/components/popup/t_popup_route.dart deleted file mode 100644 index 226f6eaf7..000000000 --- a/tdesign-component/lib/src/components/popup/t_popup_route.dart +++ /dev/null @@ -1,300 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; - -const Duration _bottomSheetEnterDuration = Duration(milliseconds: 250); -const Duration _bottomSheetExitDuration = Duration(milliseconds: 200); - -/// 从屏幕弹出的方向 -enum SlideTransitionFrom { top, right, left, bottom, center } - -/// 从屏幕的某个方向滑动弹出的Dialog框的路由,比如从顶部、底部、左、右滑出页面 -class TSlidePopupRoute extends PopupRoute { - TSlidePopupRoute({ - required this.builder, - this.barrierLabel, - this.modalBarrierColor = Colors.black54, - this.isDismissible = true, - this.modalBarrierFull = false, - this.slideTransitionFrom = SlideTransitionFrom.bottom, - this.modalWidth, - this.modalHeight, - this.modalTop = 0, - this.modalLeft = 0, - this.open, - this.opened, - this.close, - this.barrierClick, - this.focusMove = false, - }); - - /// 控件构建器 - final WidgetBuilder builder; - - /// 蒙层颜色 - final Color? modalBarrierColor; - - /// 点击蒙层能否关闭 - final bool isDismissible; - - /// 是否全屏显示蒙层 - final bool modalBarrierFull; - - /// 设置从屏幕的哪个方向滑出 - final SlideTransitionFrom slideTransitionFrom; - - /// 弹出框宽度 - final double? modalWidth; - - /// 弹出框高度 - final double? modalHeight; - - /// 弹出框顶部距离 - final double? modalTop; - - /// 弹出框左侧距离 - final double? modalLeft; - - /// 打开前事件 - final VoidCallback? open; - - /// 打开后事件 - final VoidCallback? opened; - - /// 关闭前事件 - final VoidCallback? close; - - /// 蒙层点击事件,仅在[modalBarrierFull]为false时触发 - final VoidCallback? barrierClick; - - /// 是否有输入框获取焦点时整体平移避免输入框被遮挡 - final bool focusMove; - - Color get _barrierColor => modalBarrierColor ?? Colors.black54; - - @override - Duration get transitionDuration => _bottomSheetEnterDuration; - - @override - Duration get reverseTransitionDuration => _bottomSheetExitDuration; - - @override - bool get barrierDismissible => isDismissible; - - @override - final String? barrierLabel; - - @override - Color get barrierColor => modalBarrierFull ? _barrierColor : Colors.transparent; - - /// 键盘焦点对象的Y坐标 - var _focusY = 0.0; - - /// 键盘焦点对象的高度 - var _focusHeight = 0.0; - - /// 键盘出现后bottom的偏移量 - var _lastBottom = 0.0; - - // 实现转场动画 - @override - Widget buildTransitions( - BuildContext context, - Animation animation, - Animation secondaryAnimation, - Widget child, - ) { - var animValue = decelerateEasing.transform(animation.value); - return Stack( - children: [ - if (!modalBarrierFull) - _getPositionWidget( - context, - IgnorePointer( - ignoring: true, - child: Container( - color: _barrierColor.withAlpha((animValue * _barrierColor.alpha).toInt()), - child: GestureDetector( - onTap: () { - barrierClick?.call(); - if (isDismissible) { - Navigator.pop(context); - } - }, - onDoubleTap: () { - barrierClick?.call(); - if (isDismissible) { - Navigator.pop(context); - } - }, - ), - ), - ), - ), - _getPositionWidget( - context, - Align( - alignment: slideTransitionFromToAlignment(slideTransitionFrom), - child: slideTransitionFrom != SlideTransitionFrom.center - ? FractionalTranslation( - translation: _getOffset(animValue, slideTransitionFrom), - child: ClipRect( - clipper: RectClipper(animValue, slideTransitionFrom), - child: child, - ), - ) - : Transform( - transform: Matrix4.diagonal3Values(animValue, animValue, 1), - alignment: Alignment.center, - child: child, - ), - ), - ), - ], - ); - } - - @override - Widget buildPage(BuildContext context, Animation animation, Animation secondaryAnimation) { - return Material( - child: builder.call(context), - color: Colors.transparent, - ); - } - - @override - TickerFuture didPush() { - startFocusListener(navigator!.context); - open?.call(); - animation?.addStatusListener((status) { - if (status == AnimationStatus.completed) { - opened?.call(); - } - }); - return super.didPush(); - } - - @override - void dispose() { - stopFocusListener(navigator!.context); - super.dispose(); - } - - /// 监听焦点变化 - void startFocusListener(BuildContext context) { - FocusManager.instance.addListener(_handleFocusChange); - } - - /// 停止监听焦点变化 - void stopFocusListener(BuildContext context) { - FocusManager.instance.removeListener(_handleFocusChange); - } - - void _handleFocusChange() { - // 获取当前的焦点节点 - var focusNode = FocusManager.instance.primaryFocus; - if (focusNode != null && focusNode.context != null) { - var renderObject = focusNode.context!.findRenderObject(); - if (renderObject is RenderPointerListener) { - _focusY = renderObject.localToGlobal(Offset.zero).dy; - _focusHeight = renderObject.size.height; - } - } - (focusNode?.context as Element?)?.markNeedsBuild(); - } - - @override - bool didPop(T? result) { - close?.call(); - return super.didPop(result); - } - - Widget _getPositionWidget(BuildContext context, Widget child) { - var bottom = 0.0; - var mediaQuery = MediaQuery.of(context); - if (slideTransitionFrom == SlideTransitionFrom.bottom) { - bottom = mediaQuery.viewInsets.bottom; - } else { - if ((_focusY + mediaQuery.viewInsets.bottom + _focusHeight) > mediaQuery.size.height) { - bottom = -(mediaQuery.size.height - (_focusY + mediaQuery.viewInsets.bottom + _focusHeight + 10)); - _lastBottom = bottom; - } else { - if (_lastBottom > 0.0) { - bottom = max((_lastBottom -= 5), 0).toDouble(); - } - } - } - - var screenSize = mediaQuery.size; - var _modalTop = (modalTop ?? 0).clamp(0, screenSize.height).toDouble() - (focusMove ? bottom : 0); - var _modalLeft = (modalLeft ?? 0).clamp(0, screenSize.width).toDouble(); - var _modalHeight = (modalHeight ?? screenSize.height).clamp(0, screenSize.height - _modalTop).toDouble(); - var _modalWidth = (modalWidth ?? screenSize.width).clamp(0, screenSize.width - _modalLeft).toDouble(); - return Positioned( - top: _modalTop, - bottom: screenSize.height - _modalTop - _modalHeight, - left: _modalLeft, - right: screenSize.width - _modalLeft - _modalWidth, - child: child, - ); - } - - Offset _getOffset(double animValue, SlideTransitionFrom slideTransitionFrom) { - switch (slideTransitionFrom) { - case SlideTransitionFrom.top: - return Offset(0, animValue - 1); - case SlideTransitionFrom.right: - return Offset(1 - animValue, 0); - case SlideTransitionFrom.left: - return Offset(animValue - 1, 0); - case SlideTransitionFrom.bottom: - return Offset(0, 1 - animValue); - default: - return const Offset(0, 0); - } - } -} - -Alignment slideTransitionFromToAlignment(SlideTransitionFrom from) { - switch (from) { - case SlideTransitionFrom.top: - return Alignment.topCenter; - case SlideTransitionFrom.right: - return Alignment.centerRight; - case SlideTransitionFrom.left: - return Alignment.centerLeft; - case SlideTransitionFrom.bottom: - return Alignment.bottomCenter; - case SlideTransitionFrom.center: - return Alignment.center; - } -} - -class RectClipper extends CustomClipper { - final double animValue; - final SlideTransitionFrom slideTransitionFrom; - - RectClipper(this.animValue, this.slideTransitionFrom); - - @override - Rect getClip(Size size) { - switch (slideTransitionFrom) { - case SlideTransitionFrom.top: - return Rect.fromLTWH(0, size.height * (1 - animValue), size.width, size.height); - case SlideTransitionFrom.right: - return Rect.fromLTWH(0, 0, size.width * animValue, size.height); - case SlideTransitionFrom.left: - return Rect.fromLTWH(size.width * (1 - animValue), 0, size.width, size.height); - case SlideTransitionFrom.bottom: - return Rect.fromLTWH(0, 0, size.width, size.height * animValue); - default: - return Rect.fromLTWH(0, 0, size.width, size.height); - } - } - - @override - bool shouldReclip(covariant CustomClipper oldClipper) { - return oldClipper != this; - } -} diff --git a/tdesign-component/lib/src/components/popup/t_popup_types.dart b/tdesign-component/lib/src/components/popup/t_popup_types.dart new file mode 100644 index 000000000..668ccf7cc --- /dev/null +++ b/tdesign-component/lib/src/components/popup/t_popup_types.dart @@ -0,0 +1,109 @@ +part of 't_popup.dart'; + +/// 浮层出现方向;决定 [TPopupOptions] 中哪些字段生效。 +/// +/// 与 [TPopupOptions] 类文档中的「字段与 placement」表对应。 +/// 方向固定时请用 [TPopupOptions.bottom]、[TPopupOptions.center] 等命名工厂。 +enum TPopupPlacement { + /// 自顶部滑入;使用 [TPopupOptions.height]、[TPopupOptions.inset]([TPopupTopInset])。 + top, + + /// 自左侧滑入;使用 [TPopupOptions.width]、[TPopupOptions.inset]([TPopupLeftInset])。 + left, + + /// 自右侧滑入;使用 [TPopupOptions.width]、[TPopupOptions.inset]([TPopupRightInset])。 + right, + + /// 自底部滑入;默认内置头部;使用 [TPopupOptions.height]、[TPopupOptions.inset]([TPopupBottomInset])。 + bottom, + + /// 屏幕居中;使用 [TPopupOptions.closeBuilder] 控制面板外下方关闭区。 + center, +} + +/// bottom 整行头部自定义构建器。 +/// +/// * [context] 构建上下文 +/// * [close] 关闭浮层,触发源为 [TPopupTrigger.custom] +typedef TPopupHeaderBuilder = Widget Function( + BuildContext context, + VoidCallback close, +); + +/// bottom 左右操作槽或 center 关闭区构建器。 +/// +/// * [context] 构建上下文 +/// * [close] 关闭浮层;触发源与槽位语义保持一致 +/// +/// 自定义 builder 需自行提供交互与无障碍语义;框架仅为内置默认控件补充默认语义。 +typedef TPopupSlotBuilder = Widget Function( + BuildContext context, + VoidCallback close, +); + +// 库内 sentinel:识别 builder「未传 = 内置默认」。业务三态见 [TPopupOptions]。 + +Widget _kPopupDefaultHeader(BuildContext context, VoidCallback close) => + const SizedBox.shrink(); + +Widget _kPopupDefaultCancel(BuildContext context, VoidCallback close) => + const SizedBox.shrink(); + +Widget _kPopupDefaultConfirm(BuildContext context, VoidCallback close) => + const SizedBox.shrink(); + +Widget _kPopupDefaultClose(BuildContext context, VoidCallback close) => + const SizedBox.shrink(); + +bool _isPopupDefaultHeader(TPopupHeaderBuilder? builder) => + identical(builder, _kPopupDefaultHeader); + +bool _isPopupDefaultCancel(TPopupSlotBuilder? builder) => + identical(builder, _kPopupDefaultCancel); + +bool _isPopupDefaultConfirm(TPopupSlotBuilder? builder) => + identical(builder, _kPopupDefaultConfirm); + +bool _isPopupDefaultClose(TPopupSlotBuilder? builder) => + identical(builder, _kPopupDefaultClose); + +/// 浮层关闭或显隐变化时的触发来源。 +/// +/// 作为 [TPopupVisibleChangeCallback] 的第二个参数,以及关闭流程中的语义标记。 +/// +/// 内置控件会映射为 [TPopupTrigger.overlay]、[TPopupTrigger.cancel]、 +/// [TPopupTrigger.confirm]、[TPopupTrigger.close]; +/// [TPopupHandle.close] 为 [TPopupTrigger.api];系统返回为 +/// [TPopupTrigger.systemBack];[headerBuilder] 内调用 `close` 等为 +/// [TPopupTrigger.custom]。 +enum TPopupTrigger { + /// 点击蒙层,且 [TPopupOptions.closeOnOverlayClick] 为 true。 + overlay, + + /// 点击 bottom 取消语义槽位(含默认与自定义 [TPopupOptions.cancelBuilder])。 + cancel, + + /// 点击 bottom 确认语义槽位(含默认与自定义 [TPopupOptions.confirmBuilder])。 + confirm, + + /// 点击 center 关闭语义槽位(含默认与自定义 [TPopupOptions.closeBuilder])。 + close, + + /// 外部 API 主动触发的显隐变化,如 [TPopupHandle.close] 或打开事件。 + api, + + /// 系统返回键或系统路由返回触发的关闭。 + systemBack, + + /// 无框架预设动作语义的自定义关闭,如 [headerBuilder] 内调用 `close`。 + custom, +} + +/// 浮层显隐变化回调。 +/// +/// * [visible] 为 true 表示打开,false 表示开始关闭 +/// * [trigger] 关闭来源,见 [TPopupTrigger];打开时为 [TPopupTrigger.api] +typedef TPopupVisibleChangeCallback = void Function( + bool visible, + TPopupTrigger trigger, +); diff --git a/tdesign-component/lib/src/util/t_toolbar_pressable.dart b/tdesign-component/lib/src/util/t_toolbar_pressable.dart new file mode 100644 index 000000000..7ee6d2de3 --- /dev/null +++ b/tdesign-component/lib/src/util/t_toolbar_pressable.dart @@ -0,0 +1,96 @@ +import 'package:flutter/material.dart'; + +import '../theme/t_spacers.dart'; +import '../theme/t_theme.dart'; + +/// 工具栏文字/图标按钮统一按压反馈:按下时整体透明度动画。 +/// +/// 用于 [TPicker]、[TPopup] 等「取消 | 标题 | 确认」类工具栏,后续组件请复用。 +class TToolbarPressable extends StatefulWidget { + const TToolbarPressable({ + super.key, + required this.child, + this.onTap, + this.padding, + this.enabled = true, + this.pressDuration = kToolbarPressDuration, + this.pressedOpacity = kToolbarPressedOpacity, + this.mergeTextStyle, + this.mergeIconTheme, + }); + + /// 按压动画时长(与 TDesign 工具栏规范一致)。 + static const Duration kToolbarPressDuration = Duration(milliseconds: 100); + + /// 按下时的目标透明度。 + static const double kToolbarPressedOpacity = 0.5; + + final Widget child; + final VoidCallback? onTap; + final EdgeInsetsGeometry? padding; + final bool enabled; + final Duration pressDuration; + final double pressedOpacity; + + /// 为子树 [Text] 提供默认样式(merge 语义,子控件已有样式优先)。 + final TextStyle? mergeTextStyle; + + /// 为子树 [Icon] 提供默认样式。 + final IconThemeData? mergeIconTheme; + + @override + State createState() => _TToolbarPressableState(); +} + +class _TToolbarPressableState extends State { + bool _pressed = false; + + void _setPressed(bool value) { + if (!widget.enabled || widget.onTap == null) { + return; + } + if (_pressed == value) { + return; + } + setState(() => _pressed = value); + } + + @override + Widget build(BuildContext context) { + final theme = TTheme.of(context); + final padding = widget.padding ?? + EdgeInsets.symmetric( + horizontal: theme.spacer8, + vertical: theme.spacer12, + ); + + Widget child = widget.child; + if (widget.mergeTextStyle != null) { + child = DefaultTextStyle.merge( + style: widget.mergeTextStyle!, + child: child, + ); + } + if (widget.mergeIconTheme != null) { + child = IconTheme.merge( + data: widget.mergeIconTheme!, + child: child, + ); + } + + final enabled = widget.enabled && widget.onTap != null; + + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTapDown: enabled ? (_) => _setPressed(true) : null, + onTapUp: enabled ? (_) => _setPressed(false) : null, + onTapCancel: enabled ? () => _setPressed(false) : null, + onTap: enabled ? widget.onTap : null, + child: AnimatedOpacity( + duration: widget.pressDuration, + opacity: _pressed ? widget.pressedOpacity : 1, + child: Padding(padding: padding, child: child), + ), + ); + } +} diff --git a/tdesign-component/lib/tdesign_flutter.dart b/tdesign-component/lib/tdesign_flutter.dart index 82d5e6261..548aa5a5b 100644 --- a/tdesign-component/lib/tdesign_flutter.dart +++ b/tdesign-component/lib/tdesign_flutter.dart @@ -54,8 +54,21 @@ export 'src/components/picker/t_picker_option.dart'; export 'src/components/picker/t_picker_value.dart'; export 'src/components/popover/t_popover.dart'; export 'src/components/popover/t_popover_widget.dart'; -export 'src/components/popup/t_popup_panel.dart'; -export 'src/components/popup/t_popup_route.dart'; +export 'src/components/popup/t_popup.dart' + show + TPopup, + TPopupHandle, + TPopupOptions, + TPopupPlacement, + TPopupTrigger, + TPopupInset, + TPopupTopInset, + TPopupBottomInset, + TPopupLeftInset, + TPopupRightInset, + TPopupHeaderBuilder, + TPopupSlotBuilder, + TPopupVisibleChangeCallback; export 'src/components/progress/t_progress.dart'; export 'src/components/radio/t_radio.dart'; export 'src/components/rate/t_rate.dart'; @@ -107,3 +120,4 @@ export 'src/theme/t_shadows.dart'; export 'src/theme/t_spacers.dart'; export 'src/theme/t_theme.dart'; export 'src/util/platform_util.dart'; +export 'src/util/t_toolbar_pressable.dart'; diff --git a/tdesign-component/test/helpers/popup_test_helpers.dart b/tdesign-component/test/helpers/popup_test_helpers.dart new file mode 100644 index 000000000..702cceb1d --- /dev/null +++ b/tdesign-component/test/helpers/popup_test_helpers.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import 'popup_test_resource.dart'; + +Widget wrapPopupTest( + Widget child, { + PopupTestResourceDelegate? resource, +}) { + final resolved = resource ?? PopupTestResourceDelegate.zh(); + bindPopupTestResource(resolved); + return MaterialApp( + locale: resolved.locale, + home: TTheme( + data: TThemeData.defaultData(), + child: Scaffold(body: child), + ), + ); +} + +Future openPopup( + WidgetTester tester, { + required VoidCallback onPressed, + PopupTestResourceDelegate? resource, +}) async { + await tester.pumpWidget( + wrapPopupTest( + Builder( + builder: (context) => ElevatedButton( + onPressed: onPressed, + child: const Text('open'), + ), + ), + resource: resource ?? PopupTestResourceDelegate.zh(), + ), + ); + await tester.tap(find.text('open')); +} diff --git a/tdesign-component/test/helpers/popup_test_resource.dart b/tdesign-component/test/helpers/popup_test_resource.dart new file mode 100644 index 000000000..2537875d9 --- /dev/null +++ b/tdesign-component/test/helpers/popup_test_resource.dart @@ -0,0 +1,190 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +/// Popup 测试用文案资源:仅覆盖 cancel / confirm,其余与库内默认中文一致。 +class PopupTestResourceDelegate extends TResourceDelegate { + PopupTestResourceDelegate({ + required this.cancelText, + required this.confirmText, + required this.locale, + }); + + factory PopupTestResourceDelegate.zh() => PopupTestResourceDelegate( + cancelText: '取消', + confirmText: '确定', + locale: const Locale('zh'), + ); + + factory PopupTestResourceDelegate.en() => PopupTestResourceDelegate( + cancelText: 'Cancel', + confirmText: 'Confirm', + locale: const Locale('en'), + ); + + final String cancelText; + final String confirmText; + final Locale locale; + + @override + String get cancel => cancelText; + + @override + String get confirm => confirmText; + + @override + String get open => '开'; + + @override + String get close => '关'; + + @override + String get badgeZero => '0'; + + @override + String get other => '其它'; + + @override + String get reset => '重置'; + + @override + String get loading => '加载中'; + + @override + String get loadingWithPoint => '加载中...'; + + @override + String get knew => '知道了'; + + @override + String get refreshing => '正在刷新'; + + @override + String get releaseRefresh => '松开刷新'; + + @override + String get pullToRefresh => '下拉刷新'; + + @override + String get completeRefresh => '刷新完成'; + + @override + String get days => '天'; + + @override + String get hours => '时'; + + @override + String get minutes => '分'; + + @override + String get seconds => '秒'; + + @override + String get milliseconds => '毫秒'; + + @override + String get yearLabel => '年'; + + @override + String get monthLabel => '月'; + + @override + String get dateLabel => '日'; + + @override + String get weeksLabel => '周'; + + @override + String get sunday => '日'; + + @override + String get monday => '一'; + + @override + String get tuesday => '二'; + + @override + String get wednesday => '三'; + + @override + String get thursday => '四'; + + @override + String get friday => '五'; + + @override + String get saturday => '六'; + + @override + String get year => ' 年'; + + @override + String get january => '1 月'; + + @override + String get february => '2 月'; + + @override + String get march => '3 月'; + + @override + String get april => '4 月'; + + @override + String get may => '5 月'; + + @override + String get june => '6 月'; + + @override + String get july => '7 月'; + + @override + String get august => '8 月'; + + @override + String get september => '9 月'; + + @override + String get october => '10 月'; + + @override + String get november => '11 月'; + + @override + String get december => '12 月'; + + @override + String get time => '时间'; + + @override + String get start => '开始'; + + @override + String get end => '结束'; + + @override + String get notRated => '未评分'; + + @override + String get cascadeLabel => '选择选项'; + + @override + String get back => '返回'; + + @override + String get top => '顶部'; + + @override + String get emptyData => '暂无数据'; +} + +/// 在测试中注入 [resource];与业务侧 `TTheme.setResourceBuilder` 用法一致。 +void bindPopupTestResource(PopupTestResourceDelegate resource) { + TTheme.setResourceBuilder((_) => resource, needAlwaysBuild: true); +} + +/// 恢复为库内默认资源(中文 cancel/confirm)。 +void resetPopupTestResource() { + TTheme.setResourceBuilder((_) => null, needAlwaysBuild: false); +} diff --git a/tdesign-component/test/t_bottom_tab_bar_test.dart b/tdesign-component/test/t_bottom_tab_bar_test.dart index 99cd97514..b64934bcf 100644 --- a/tdesign-component/test/t_bottom_tab_bar_test.dart +++ b/tdesign-component/test/t_bottom_tab_bar_test.dart @@ -64,7 +64,8 @@ void main() { group('TBottomTabBar — iconText 图标颜色 (issue #900)', () { // TC-01: iconText 选中 tab 图标颜色为 brandNormalColor testWidgets('TC-01: 选中 tab 的图标颜色应为 brandNormalColor', (tester) async { - await tester.pumpWidget(_buildTestApp(_buildIconTextTabBar(currentIndex: 0))); + await tester + .pumpWidget(_buildTestApp(_buildIconTextTabBar(currentIndex: 0))); await tester.pumpAndSettle(); final BuildContext context = tester.element(find.byType(TBottomTabBar)); @@ -75,7 +76,8 @@ void main() { expect(icons, isNotEmpty); // 通过 IconTheme 验证选中图标的颜色 - final iconThemes = tester.widgetList(find.byType(IconTheme)).toList(); + final iconThemes = + tester.widgetList(find.byType(IconTheme)).toList(); expect(iconThemes, isNotEmpty, reason: '选中图标应被 IconTheme 包裹以注入颜色'); // 验证至少有一个 IconTheme 的 color 是 brandNormalColor @@ -91,13 +93,15 @@ void main() { // TC-02: iconText 未选中 tab 图标颜色为 textColorPrimary testWidgets('TC-02: 未选中 tab 的图标颜色应为 textColorPrimary', (tester) async { - await tester.pumpWidget(_buildTestApp(_buildIconTextTabBar(currentIndex: 0))); + await tester + .pumpWidget(_buildTestApp(_buildIconTextTabBar(currentIndex: 0))); await tester.pumpAndSettle(); final BuildContext context = tester.element(find.byType(TBottomTabBar)); final expectedColor = TTheme.of(context).textColorPrimary; - final iconThemes = tester.widgetList(find.byType(IconTheme)).toList(); + final iconThemes = + tester.widgetList(find.byType(IconTheme)).toList(); expect(iconThemes, isNotEmpty, reason: '未选中图标应被 IconTheme 包裹以注入颜色'); final unselectedIconTheme = iconThemes.firstWhere( @@ -150,24 +154,29 @@ void main() { await tester.tap(find.text('我的')); await tester.pumpAndSettle(); - final iconThemesAfter = tester.widgetList(find.byType(IconTheme)).toList(); + final iconThemesAfter = + tester.widgetList(find.byType(IconTheme)).toList(); final colors = iconThemesAfter.map((t) => t.data.color).toSet(); - expect(colors, contains(brandColor), reason: '切换后新选中 tab 图标应为 brandNormalColor'); - expect(colors, contains(primaryColor), reason: '切换后旧 tab 图标应为 textColorPrimary'); + expect(colors, contains(brandColor), + reason: '切换后新选中 tab 图标应为 brandNormalColor'); + expect(colors, contains(primaryColor), + reason: '切换后旧 tab 图标应为 textColorPrimary'); }); }); group('TBottomTabBar — icon 类型图标颜色 (issue #900 同类问题)', () { // TC-04: icon 类型选中 tab 图标颜色为 brandNormalColor - testWidgets('TC-04: icon 类型 — 选中 tab 图标颜色应为 brandNormalColor', (tester) async { + testWidgets('TC-04: icon 类型 — 选中 tab 图标颜色应为 brandNormalColor', + (tester) async { await tester.pumpWidget(_buildTestApp(_buildIconTabBar(currentIndex: 0))); await tester.pumpAndSettle(); final BuildContext context = tester.element(find.byType(TBottomTabBar)); final expectedColor = TTheme.of(context).brandNormalColor; - final iconThemes = tester.widgetList(find.byType(IconTheme)).toList(); + final iconThemes = + tester.widgetList(find.byType(IconTheme)).toList(); expect(iconThemes, isNotEmpty); final match = iconThemes.firstWhere( @@ -181,14 +190,16 @@ void main() { }); // TC-05: icon 类型未选中 tab 图标颜色为 textColorPrimary - testWidgets('TC-05: icon 类型 — 未选中 tab 图标颜色应为 textColorPrimary', (tester) async { + testWidgets('TC-05: icon 类型 — 未选中 tab 图标颜色应为 textColorPrimary', + (tester) async { await tester.pumpWidget(_buildTestApp(_buildIconTabBar(currentIndex: 0))); await tester.pumpAndSettle(); final BuildContext context = tester.element(find.byType(TBottomTabBar)); final expectedColor = TTheme.of(context).textColorPrimary; - final iconThemes = tester.widgetList(find.byType(IconTheme)).toList(); + final iconThemes = + tester.widgetList(find.byType(IconTheme)).toList(); final match = iconThemes.firstWhere( (t) => t.data.color == expectedColor, orElse: () => throw TestFailure( @@ -203,7 +214,8 @@ void main() { group('TBottomTabBar — 回归检查', () { // TC-06: 文字颜色不受影响 testWidgets('TC-06: iconText 类型文字颜色回归验证', (tester) async { - await tester.pumpWidget(_buildTestApp(_buildIconTextTabBar(currentIndex: 0))); + await tester + .pumpWidget(_buildTestApp(_buildIconTextTabBar(currentIndex: 0))); await tester.pumpAndSettle(); final BuildContext context = tester.element(find.byType(TBottomTabBar)); @@ -217,14 +229,16 @@ void main() { // 找到 textColor 为 brandColor 的文字(选中) final selectedText = tTexts.firstWhere( (t) => t.textColor == brandColor, - orElse: () => throw TestFailure('未找到 textColor == brandNormalColor 的 TText'), + orElse: () => + throw TestFailure('未找到 textColor == brandNormalColor 的 TText'), ); expect(selectedText.textColor, equals(brandColor)); // 找到 textColor 为 primaryColor 的文字(未选中) final unselectedText = tTexts.firstWhere( (t) => t.textColor == primaryColor, - orElse: () => throw TestFailure('未找到 textColor == textColorPrimary 的 TText'), + orElse: () => + throw TestFailure('未找到 textColor == textColorPrimary 的 TText'), ); expect(unselectedText.textColor, equals(primaryColor)); }); diff --git a/tdesign-component/test/t_picker_test.dart b/tdesign-component/test/t_picker_test.dart index d3da36c71..b5c57eb1d 100644 --- a/tdesign-component/test/t_picker_test.dart +++ b/tdesign-component/test/t_picker_test.dart @@ -399,8 +399,7 @@ void main() { expect(find.byType(ListWheelScrollView), findsNWidgets(3)); }); - testWidgets('初始值非首项 - onChange 返回正确值(多列独立)', - (WidgetTester tester) async { + testWidgets('初始值非首项 - onChange 返回正确值(多列独立)', (WidgetTester tester) async { TPickerValue? captured; const testData = [ [ @@ -438,8 +437,7 @@ void main() { expect(captured!.labels[1], 'B2'); }); - testWidgets('初始值非首项 - 联动模式 onChange 返回正确值', - (WidgetTester tester) async { + testWidgets('初始值非首项 - 联动模式 onChange 返回正确值', (WidgetTester tester) async { TPickerValue? captured; final linkedData = { const TPickerOption(label: '广东省', value: 'GD'): { @@ -478,8 +476,7 @@ void main() { expect(captured!.values[1], 'SZ'); }); - testWidgets('disabled 项修正 - 独立模式滚动不会 crash', - (WidgetTester tester) async { + testWidgets('disabled 项修正 - 独立模式滚动不会 crash', (WidgetTester tester) async { TPickerValue? captured; const testData = [ [ @@ -512,8 +509,7 @@ void main() { } }); - testWidgets('didUpdateWidget - items 变化触发重建', - (WidgetTester tester) async { + testWidgets('didUpdateWidget - items 变化触发重建', (WidgetTester tester) async { const testData1 = [ [TPickerOption(label: 'A', value: 'a')], ]; @@ -548,8 +544,7 @@ void main() { expect(find.byType(TPicker), findsOneWidget); }); - testWidgets('didUpdateWidget - items 变化触发重建', - (WidgetTester tester) async { + testWidgets('didUpdateWidget - items 变化触发重建', (WidgetTester tester) async { const testData = [ [ TPickerOption(label: 'A', value: 'a'), @@ -629,8 +624,7 @@ void main() { expect(find.byType(TPicker), findsOneWidget); }); - testWidgets('联动模式 - 列数变化后能恢复', - (WidgetTester tester) async { + testWidgets('联动模式 - 列数变化后能恢复', (WidgetTester tester) async { final linkedData = { const TPickerOption(label: '广东省', value: 'GD'): { const TPickerOption(label: '深圳市', value: 'SZ'): const [ @@ -679,8 +673,7 @@ void main() { expect(find.byType(ListWheelScrollView), findsNWidgets(3)); }); - testWidgets('联动模式 - 滚动后新列选中首项', - (WidgetTester tester) async { + testWidgets('联动模式 - 滚动后新列选中首项', (WidgetTester tester) async { TPickerValue? captured; final linkedData = { const TPickerOption(label: '广东省', value: 'GD'): { @@ -689,7 +682,8 @@ void main() { TPickerOption(label: '福田区', value: 'FT'), TPickerOption(label: '罗湖区', value: 'LL'), ], - const TPickerOption(label: '广州市', value: 'GZ'): const [], + const TPickerOption(label: '广州市', value: 'GZ'): + const [], }, }; @@ -723,8 +717,7 @@ void main() { expect(captured!.values[2], 'NS'); }); - testWidgets('onLoad 回调 - 滚动时触发', - (WidgetTester tester) async { + testWidgets('onLoad 回调 - 滚动时触发', (WidgetTester tester) async { var loadCallCount = 0; int? capturedColumn; int? capturedRemaining; @@ -765,8 +758,7 @@ void main() { expect(capturedRemaining, greaterThanOrEqualTo(0)); }); - testWidgets('onLoad 回调 - 联动模式 parentValue 正确', - (WidgetTester tester) async { + testWidgets('onLoad 回调 - 联动模式 parentValue 正确', (WidgetTester tester) async { dynamic capturedParentValue; int? capturedColumn; final linkedData = { @@ -915,7 +907,8 @@ void main() { [TPickerOption(label: 'A', value: 'a')], [TPickerOption(label: 'B', value: 'b')], ]; - final result = TPickerNormalize.normalizeColumns(input, TPickerKeys.defaults); + final result = + TPickerNormalize.normalizeColumns(input, TPickerKeys.defaults); expect(identical(result, input), true); }); @@ -923,7 +916,8 @@ void main() { final input = { const TPickerOption(label: 'A', value: 'a'): null, }; - final result = TPickerNormalize.normalizeLinked(input, TPickerKeys.defaults); + final result = + TPickerNormalize.normalizeLinked(input, TPickerKeys.defaults); expect(identical(result, input), true); }); @@ -937,7 +931,8 @@ void main() { {'label': 'C', 'value': 'c'}, ], ]; - final result = TPickerNormalize.normalizeColumns(input, TPickerKeys.defaults); + final result = + TPickerNormalize.normalizeColumns(input, TPickerKeys.defaults); expect(result, isA>>()); expect(result.length, 2); expect(result[0].length, 2); @@ -952,7 +947,8 @@ void main() { ], }, }; - final result = TPickerNormalize.normalizeLinked(input, const TPickerKeys(label: 'label', value: 'value')); + final result = TPickerNormalize.normalizeLinked( + input, const TPickerKeys(label: 'label', value: 'value')); expect(result, isA>()); }); @@ -966,7 +962,8 @@ void main() { ], }, }; - final result = TPickerNormalize.normalizeLinked(input, TPickerKeys.defaults); + final result = + TPickerNormalize.normalizeLinked(input, TPickerKeys.defaults); expect(result, isA>()); final keyA = result.keys.first; @@ -1009,8 +1006,7 @@ void main() { expect(find.byType(TPicker), findsOneWidget); }); - testWidgets('disabled 项修正 - 正向查找最近 enabled', - (WidgetTester tester) async { + testWidgets('disabled 项修正 - 正向查找最近 enabled', (WidgetTester tester) async { TPickerValue? captured; const testData = [ [ @@ -1042,8 +1038,7 @@ void main() { } }); - testWidgets('disabled 项修正 - 反向查找最近 enabled', - (WidgetTester tester) async { + testWidgets('disabled 项修正 - 反向查找最近 enabled', (WidgetTester tester) async { TPickerValue? captured; const testData = [ [ @@ -1080,7 +1075,8 @@ void main() { ['北京', '上海', '广州'], ['朝阳区', '浦东'], ]; - final result = TPickerNormalize.normalizeColumns(input, TPickerKeys.defaults); + final result = + TPickerNormalize.normalizeColumns(input, TPickerKeys.defaults); expect(result, isA>>()); expect(result[0][0].label, '北京'); expect(result[0][0].value, '北京'); // 纯字符串时 value == label @@ -1088,21 +1084,26 @@ void main() { }); test('TPickerNormalize - 空列表归一化', () { - final result = TPickerNormalize.normalizeColumns([], TPickerKeys.defaults); + final result = + TPickerNormalize.normalizeColumns([], TPickerKeys.defaults); expect(result, isA>>()); expect(result.isEmpty, true); }); test('TPickerNormalize - 空 Map 归一化', () { - final result = - TPickerNormalize.normalizeLinked({}, TPickerKeys.defaults); + final result = TPickerNormalize.normalizeLinked( + {}, TPickerKeys.defaults); expect(result, isA>()); expect(result.isEmpty, true); }); test('TPickerNormalize - List 中包含非 List 元素得到空列', () { - final input = ['not_a_list', ['A', 'B']]; - final result = TPickerNormalize.normalizeColumns(input, TPickerKeys.defaults); + final input = [ + 'not_a_list', + ['A', 'B'] + ]; + final result = + TPickerNormalize.normalizeColumns(input, TPickerKeys.defaults); expect(result, isA>>()); expect(result[0], isEmpty); // 非 List 元素归一化为空列 expect(result[1].length, 2); @@ -1115,7 +1116,8 @@ void main() { {'name': '广州', 'code': 'GZ'}, ], ]; - const keys = TPickerKeys(label: 'name', value: 'code', disabled: 'readonly'); + const keys = + TPickerKeys(label: 'name', value: 'code', disabled: 'readonly'); final result = TPickerNormalize.normalizeColumns(input, keys); expect(result[0][0].label, '深圳'); expect(result[0][0].value, 'SZ'); @@ -1127,7 +1129,8 @@ void main() { final input = { 'A': ['X', 'Y'], }; - final result = TPickerNormalize.normalizeLinked(input, TPickerKeys.defaults); + final result = + TPickerNormalize.normalizeLinked(input, TPickerKeys.defaults); expect(result, isA>()); final keyA = result.keys.first; expect(keyA.label, 'A'); @@ -1141,7 +1144,8 @@ void main() { final input = { 'A': 12345, // 既不是 Map 也不是 List }; - final result = TPickerNormalize.normalizeLinked(input, TPickerKeys.defaults); + final result = + TPickerNormalize.normalizeLinked(input, TPickerKeys.defaults); final child = result[result.keys.first]; expect(child, isA>()); expect((child as List).isEmpty, true); @@ -1154,7 +1158,8 @@ void main() { {'label': 'Y', 'value': 'y'}, ], ]; - final result = TPickerNormalize.normalizeColumns(input, TPickerKeys.defaults); + final result = + TPickerNormalize.normalizeColumns(input, TPickerKeys.defaults); expect(result[0][0].label, 'X'); expect(result[0][1].label, 'Y'); }); @@ -1163,7 +1168,8 @@ void main() { final input = { null: ['child1'], }; - final result = TPickerNormalize.normalizeLinked(input, TPickerKeys.defaults); + final result = + TPickerNormalize.normalizeLinked(input, TPickerKeys.defaults); final key = result.keys.first; expect(key.label, ''); expect(key.value, null); @@ -1250,14 +1256,18 @@ void main() { expect(columns.columns[0][0].label, '北京'); final linked = TPickerLinked.fromRaw(const { - '广东': {'深圳': ['南山']}, + '广东': { + '深圳': ['南山'] + }, }); expect(linked.tree.keys.first.label, '广东'); }); test('TPickerKeys - 值相同的不同实例 hashCode 一致', () { - const a = TPickerKeys(label: 'x', value: 'y', disabled: 'd', children: 'c'); - const b = TPickerKeys(label: 'x', value: 'y', disabled: 'd', children: 'c'); + const a = + TPickerKeys(label: 'x', value: 'y', disabled: 'd', children: 'c'); + const b = + TPickerKeys(label: 'x', value: 'y', disabled: 'd', children: 'c'); expect(a == b, true); expect(a.hashCode, b.hashCode); }); @@ -1292,7 +1302,8 @@ void main() { Text('selected: $selected'), TPicker( items: const TPickerColumns(testData), - onChange: (v) => setState(() => selected = v.values.first as String), + onChange: (v) => + setState(() => selected = v.values.first as String), ), ], ); @@ -1349,7 +1360,8 @@ void main() { TPicker( items: TPickerLinked(linkedData), initialValue: const ['GD', 'SZ', 'NS'], - onChange: (v) => setState(() => selected = v.labels.join(' / ')), + onChange: (v) => + setState(() => selected = v.labels.join(' / ')), ), ], ); @@ -1413,8 +1425,7 @@ void main() { expect(a == c, false); }); - testWidgets('工具栏 - 默认显示取消/确认按钮', - (WidgetTester tester) async { + testWidgets('工具栏 - 默认显示取消/确认按钮', (WidgetTester tester) async { await tester.pumpWidget( MaterialApp( home: Scaffold( @@ -1435,8 +1446,7 @@ void main() { expect(find.text('确认'), findsOneWidget); }); - testWidgets('工具栏 - 设置 title 后中部显示标题', - (WidgetTester tester) async { + testWidgets('工具栏 - 设置 title 后中部显示标题', (WidgetTester tester) async { await tester.pumpWidget( MaterialApp( home: Scaffold( @@ -1459,8 +1469,7 @@ void main() { expect(find.text('请选择'), findsOneWidget); }); - testWidgets('工具栏 - 点击取消触发 onCancel', - (WidgetTester tester) async { + testWidgets('工具栏 - 点击取消触发 onCancel', (WidgetTester tester) async { var cancelled = false; await tester.pumpWidget( MaterialApp( @@ -1481,8 +1490,7 @@ void main() { expect(cancelled, true); }); - testWidgets('工具栏 - 点击确认触发 onConfirm 并携带选中值', - (WidgetTester tester) async { + testWidgets('工具栏 - 点击确认触发 onConfirm 并携带选中值', (WidgetTester tester) async { TPickerValue? confirmedValue; await tester.pumpWidget( MaterialApp( @@ -1533,8 +1541,7 @@ void main() { expect(find.text('确认'), findsNothing); }); - testWidgets('工具栏 - cancel 自定义插槽(图标)', - (WidgetTester tester) async { + testWidgets('工具栏 - cancel 自定义插槽(图标)', (WidgetTester tester) async { var cancelled = false; await tester.pumpWidget( MaterialApp( @@ -1558,8 +1565,7 @@ void main() { expect(cancelled, true); }); - testWidgets('工具栏 - confirm 自定义插槽(图标)', - (WidgetTester tester) async { + testWidgets('工具栏 - confirm 自定义插槽(图标)', (WidgetTester tester) async { TPickerValue? confirmedValue; await tester.pumpWidget( MaterialApp( @@ -1590,8 +1596,7 @@ void main() { expect(confirmedValue!.labels.first, 'Y'); }); - testWidgets('工具栏 - titleWidget 自定义标题插槽', - (WidgetTester tester) async { + testWidgets('工具栏 - titleWidget 自定义标题插槽', (WidgetTester tester) async { await tester.pumpWidget( const MaterialApp( home: Scaffold( diff --git a/tdesign-component/test/t_popup_coverage_test.dart b/tdesign-component/test/t_popup_coverage_test.dart new file mode 100644 index 000000000..295d6cb84 --- /dev/null +++ b/tdesign-component/test/t_popup_coverage_test.dart @@ -0,0 +1,1238 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:tdesign_flutter/src/components/popup/t_popup.dart' + show PopupHeader, PopupLayout; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import 'helpers/popup_test_helpers.dart'; +import 'helpers/popup_test_resource.dart'; + +void main() { + tearDown(resetPopupTestResource); + + group('TPopup 覆盖率补充', () { + testWidgets('bottom 仅标题行(无操作栏)', (tester) async { + await openPopup( + tester, + onPressed: () { + TPopup.show( + tester.element(find.text('open')), + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 160, + titleWidget: TText('仅标题行'), + cancelBuilder: null, + confirmBuilder: null, + child: const SizedBox(height: 60)), + ); + }, + ); + await tester.pumpAndSettle(); + expect(find.text('仅标题行'), findsOneWidget); + expect(find.text('取消'), findsNothing); + expect(find.text('确定'), findsNothing); + }); + + testWidgets('bottom 仅标题时可正常渲染标题内容', (tester) async { + await openPopup( + tester, + onPressed: () { + TPopup.show( + tester.element(find.text('open')), + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 160, + titleWidget: TText('左对齐标题'), + cancelBuilder: null, + confirmBuilder: null, + child: const SizedBox(height: 60)), + ); + }, + ); + await tester.pumpAndSettle(); + expect(find.text('左对齐标题'), findsOneWidget); + }); + + testWidgets('bottom 仅隐藏 confirm 槽位', (tester) async { + await openPopup( + tester, + onPressed: () { + TPopup.show( + tester.element(find.text('open')), + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 160, + confirmBuilder: null, + child: const SizedBox(height: 60)), + ); + }, + ); + await tester.pumpAndSettle(); + expect(find.text('取消'), findsOneWidget); + expect(find.text('确定'), findsNothing); + }); + + testWidgets('bottom 仅隐藏 cancel 槽位', (tester) async { + await openPopup( + tester, + onPressed: () { + TPopup.show( + tester.element(find.text('open')), + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 160, + cancelBuilder: null, + child: const SizedBox(height: 60)), + ); + }, + ); + await tester.pumpAndSettle(); + expect(find.text('取消'), findsNothing); + expect(find.text('确定'), findsOneWidget); + }); + + testWidgets('center closeBuilder 自定义关闭区', (tester) async { + late BuildContext hostContext; + await openPopup( + tester, + onPressed: () { + hostContext = tester.element(find.text('open')); + TPopup.show( + hostContext, + options: TPopupOptions( + placement: TPopupPlacement.center, + width: 160, + height: 120, + closeBuilder: (_, close) => TextButton( + onPressed: close, + child: const Text('builder关闭'), + ), + child: const SizedBox(height: 80, width: 120)), + ); + }, + ); + await tester.pumpAndSettle(); + expect(find.text('builder关闭'), findsOneWidget); + await tester.tap(find.text('builder关闭')); + await tester.pumpAndSettle(); + }); + + testWidgets('center 默认关闭按钮点击关闭浮层', (tester) async { + late BuildContext hostContext; + await openPopup( + tester, + onPressed: () { + hostContext = tester.element(find.text('open')); + TPopup.show( + hostContext, + options: TPopupOptions( + placement: TPopupPlacement.center, + width: 120, + height: 120, + child: const SizedBox(height: 80, width: 80)), + ); + }, + ); + await tester.pumpAndSettle(); + expect(find.byIcon(TIcons.close_circle), findsOneWidget); + await tester.tap(find.byIcon(TIcons.close_circle)); + await tester.pumpAndSettle(); + expect(find.byIcon(TIcons.close_circle), findsNothing); + }); + + testWidgets('bottom 无固定 height 贴底布局', (tester) async { + await openPopup( + tester, + onPressed: () { + TPopup.show( + tester.element(find.text('open')), + options: TPopupOptions( + placement: TPopupPlacement.bottom, + cancelBuilder: null, + confirmBuilder: null, + child: const SizedBox(height: 80, width: 200)), + ); + }, + ); + await tester.pumpAndSettle(); + expect(find.byType(Positioned), findsWidgets); + }); + + testWidgets('top 无固定 height 可打开', (tester) async { + await openPopup( + tester, + onPressed: () { + TPopup.show( + tester.element(find.text('open')), + options: TPopupOptions( + placement: TPopupPlacement.top, + child: const SizedBox(height: 60, width: 200)), + ); + }, + ); + await tester.pumpAndSettle(); + expect(find.byType(Positioned), findsWidgets); + }); + + testWidgets('center closeBuilder=null 无下方关闭', (tester) async { + await openPopup( + tester, + onPressed: () { + TPopup.show( + tester.element(find.text('open')), + options: TPopupOptions( + placement: TPopupPlacement.center, + width: 100, + height: 100, + closeBuilder: null, + child: const SizedBox(height: 80, width: 80)), + ); + }, + ); + await tester.pumpAndSettle(); + expect(find.byIcon(TIcons.close_circle), findsNothing); + expect(find.byType(Center), findsWidgets); + }); + + testWidgets('handle 重复 close 安全', (tester) async { + TPopupHandle? handle; + await openPopup( + tester, + onPressed: () { + handle = TPopup.show( + tester.element(find.text('open')), + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 80, + cancelBuilder: null, + confirmBuilder: null, + child: const SizedBox(height: 40)), + ); + }, + ); + await tester.pumpAndSettle(); + handle!.close(); + handle!.close(); + await tester.pumpAndSettle(); + expect(handle!.isShowing, isFalse); + }); + + testWidgets('handle.open 关闭后再次打开触发 onOpen / onOpened', (tester) async { + var openCount = 0; + var openedCount = 0; + late BuildContext hostContext; + TPopupHandle? handle; + + await openPopup( + tester, + onPressed: () { + hostContext = tester.element(find.text('open')); + handle = TPopup.show( + hostContext, + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 80, + cancelBuilder: null, + confirmBuilder: null, + onOpen: () => openCount++, + onOpened: () => openedCount++, + child: const SizedBox(height: 40)), + ); + }, + ); + await tester.pumpAndSettle(); + expect(openCount, 1); + expect(openedCount, 1); + + handle!.close(); + await tester.pumpAndSettle(); + + handle!.open(hostContext); + await tester.pumpAndSettle(); + expect(openCount, 2); + expect(openedCount, 2); + }); + + testWidgets('TToolbarPressable 按压与禁用', (tester) async { + var tapped = false; + await tester.pumpWidget( + MaterialApp( + home: TTheme( + data: TThemeData.defaultData(), + child: Scaffold( + body: TToolbarPressable( + onTap: () => tapped = true, + child: const Text('press'), + ), + ), + ), + ), + ); + await tester.tap(find.text('press')); + await tester.pumpAndSettle(); + expect(tapped, isTrue); + + await tester.pumpWidget( + MaterialApp( + home: TTheme( + data: TThemeData.defaultData(), + child: Scaffold( + body: TToolbarPressable( + onTap: null, + mergeTextStyle: const TextStyle(color: Colors.red), + mergeIconTheme: const IconThemeData(color: Colors.red), + child: const Icon(Icons.add), + ), + ), + ), + ), + ); + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + }); + }); + + group('TPopupOptions 覆盖率补充', () { + test('hasBuiltInHeader 识别 titleWidget', () { + expect( + TPopupOptions( + child: const SizedBox(), + placement: TPopupPlacement.bottom, + titleWidget: const Text('w'), + cancelBuilder: null, + confirmBuilder: null, + ).hasBuiltInHeader, + isTrue, + ); + }); + + test('useCustomHeader 与 useDefaultHeader 互斥', () { + final custom = TPopupOptions( + child: const SizedBox(), + placement: TPopupPlacement.bottom, + headerBuilder: (_, __) => const Text('h'), + ); + expect(custom.useCustomHeader, isTrue); + expect(custom.useDefaultHeader, isFalse); + + final titleOnly = TPopupOptions( + child: const SizedBox(), + placement: TPopupPlacement.bottom, + titleWidget: TText('仅标题'), + cancelBuilder: null, + confirmBuilder: null, + ); + expect(titleOnly.useDefaultHeader, isTrue); + expect(titleOnly.useCustomHeader, isFalse); + expect(titleOnly.hasBuiltInHeader, isTrue); + }); + + test('assertPlacementParams 覆盖各 placement 的字段提示', () { + // bottom + width → 抛 FlutterError + expect( + () => TPopupOptions( + child: const SizedBox(), + placement: TPopupPlacement.bottom, + width: 200, + ).assertPlacementParams(), + throwsA(isA()), + ); + // top 默认配置 → 不抛 + expect( + () => TPopupOptions( + child: const SizedBox(), + placement: TPopupPlacement.top, + ).assertPlacementParams(), + returnsNormally, + ); + // center 合法字段 → 不抛 + expect( + () => TPopupOptions( + child: const SizedBox(), + placement: TPopupPlacement.center, + height: 100, + closeBuilder: null, + ).assertPlacementParams(), + returnsNormally, + ); + expect( + () => TPopupOptions( + child: const SizedBox(), + placement: TPopupPlacement.center, + ).assertPlacementParams(), + returnsNormally, + ); + }); + }); + + group('TPopup 覆盖率深化', () { + testWidgets('headerBuilder 自定义内容可正常渲染', + (tester) async { + await openPopup( + tester, + onPressed: () { + TPopup.show( + tester.element(find.text('open')), + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 160, + headerBuilder: (ctx, close) => Column( + children: const [ + Text('头Widget'), + Row( + children: [ + Text('builder左'), + Text('builder右'), + ], + ), + ], + ), + child: const SizedBox(height: 60)), + ); + }, + ); + await tester.pumpAndSettle(); + expect(find.text('头Widget'), findsOneWidget); + expect(find.text('builder左'), findsOneWidget); + expect(find.text('builder右'), findsOneWidget); + }); + + testWidgets('headerBuilder 自定义行内内容可正常渲染', (tester) async { + await openPopup( + tester, + onPressed: () { + TPopup.show( + tester.element(find.text('open')), + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 160, + headerBuilder: (ctx, close) => Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: const [ + Text('左槽Widget'), + Text('右槽Widget'), + ], + ), + child: const SizedBox(height: 60)), + ); + }, + ); + await tester.pumpAndSettle(); + expect(find.text('左槽Widget'), findsOneWidget); + expect(find.text('右槽Widget'), findsOneWidget); + }); + + testWidgets('操作栏使用自定义 cancel/confirm Widget(非 Builder)', (tester) async { + await openPopup( + tester, + onPressed: () { + TPopup.show( + tester.element(find.text('open')), + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 160, + cancelBuilder: (_, __) => const Text('自定义左'), + confirmBuilder: (_, __) => const Text('自定义右'), + child: const SizedBox(height: 60)), + ); + }, + ); + await tester.pumpAndSettle(); + expect(find.text('自定义左'), findsOneWidget); + expect(find.text('自定义右'), findsOneWidget); + }); + + testWidgets('onVisibleChange 记录各关闭触发源', (tester) async { + final hideTriggers = []; + late BuildContext hostContext; + + await openPopup( + tester, + onPressed: () { + hostContext = tester.element(find.text('open')); + TPopup.show( + hostContext, + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 160, + titleWidget: TText('标题'), + onVisibleChange: (visible, trigger) { + if (!visible) { + hideTriggers.add(trigger); + } + }, + child: const SizedBox(height: 60)), + ); + }, + ); + await tester.pumpAndSettle(); + + await tester.tap(find.text('取消')); + await tester.pumpAndSettle(); + expect(hideTriggers.last, TPopupTrigger.cancel); + + await openPopup( + tester, + onPressed: () { + TPopup.show( + hostContext, + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 160, + onVisibleChange: (visible, trigger) { + if (!visible) { + hideTriggers.add(trigger); + } + }, + child: const SizedBox(height: 60)), + ); + }, + ); + await tester.pumpAndSettle(); + await tester.tapAt(const Offset(10, 10)); + await tester.pumpAndSettle(); + expect(hideTriggers.last, TPopupTrigger.overlay); + + await openPopup( + tester, + onPressed: () { + TPopup.show( + hostContext, + options: TPopupOptions( + placement: TPopupPlacement.center, + width: 120, + height: 120, + onVisibleChange: (visible, trigger) { + if (!visible) { + hideTriggers.add(trigger); + } + }, + child: const SizedBox(height: 80, width: 80)), + ); + }, + ); + await tester.pumpAndSettle(); + await tester.tap(find.byIcon(TIcons.close_circle)); + await tester.pumpAndSettle(); + expect(hideTriggers.last, TPopupTrigger.close); + }); + + testWidgets('Popup 内嵌套 show 可再开一层且先关内层', (tester) async { + TPopupHandle? outerHandle; + TPopupHandle? innerHandle; + + await openPopup( + tester, + onPressed: () { + outerHandle = TPopup.show( + tester.element(find.text('open')), + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 200, + cancelBuilder: null, + confirmBuilder: null, + child: Builder( + builder: (ctx) { + return ElevatedButton( + onPressed: () { + innerHandle = TPopup.show( + ctx, + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 120, + cancelBuilder: null, + confirmBuilder: null, + child: const Text('内层'), + ), + ); + }, + child: const Text('开内层'), + ); + }, + )), + ); + }, + ); + await tester.pumpAndSettle(); + expect(find.text('开内层'), findsOneWidget); + + await tester.tap(find.text('开内层')); + await tester.pumpAndSettle(); + expect(find.text('内层'), findsOneWidget); + + innerHandle!.close(); + await tester.pumpAndSettle(); + expect(find.text('内层'), findsNothing); + expect(find.text('开内层'), findsOneWidget); + + outerHandle!.close(); + await tester.pumpAndSettle(); + }); + + testWidgets('modal 为 false 仍可打开关闭', (tester) async { + TPopupHandle? handle; + await openPopup( + tester, + onPressed: () { + handle = TPopup.show( + tester.element(find.text('open')), + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 120, + showOverlay: false, + modal: false, + child: const SizedBox(height: 60)), + ); + }, + ); + await tester.pumpAndSettle(); + handle!.close(); + await tester.pumpAndSettle(); + }); + + testWidgets('center 自定义 radius 与 backgroundColor', (tester) async { + await openPopup( + tester, + onPressed: () { + TPopup.show( + tester.element(find.text('open')), + options: TPopupOptions( + placement: TPopupPlacement.center, + width: 100, + height: 100, + radius: 4, + backgroundColor: Colors.red, + child: const SizedBox(height: 60, width: 60)), + ); + }, + ); + await tester.pumpAndSettle(); + final hasRedPanel = + tester.widgetList(find.byType(Container)).any((c) { + final d = c.decoration; + return d is BoxDecoration && d.color == Colors.red; + }); + expect(hasRedPanel, isTrue); + }); + + testWidgets('bottom inset.left/right 与无 overlay 仍可关闭', (tester) async { + TPopupHandle? handle; + await openPopup( + tester, + onPressed: () { + handle = TPopup.show( + tester.element(find.text('open')), + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 100, + inset: const TPopupBottomInset(left: 16, right: 16), + showOverlay: false, + cancelBuilder: null, + confirmBuilder: null, + child: const SizedBox(height: 60)), + ); + }, + ); + await tester.pumpAndSettle(); + handle!.close(); + await tester.pumpAndSettle(); + }); + }); + + group('PopupLayout 覆盖率补充', () { + testWidgets('bottom 无 height 时贴底', (tester) async { + final layout = PopupLayout( + placement: TPopupPlacement.bottom, + ); + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: Stack( + children: [ + layout.wrapPositioned(child: const SizedBox(height: 1)) + ], + ), + ), + ), + ); + final positioned = tester.widget(find.byType(Positioned)); + expect(positioned.bottom, 0); + expect(positioned.top, isNull); + expect(positioned.height, isNull); + }); + + test('alignment center', () { + expect( + PopupLayout( + placement: TPopupPlacement.center, + ).alignment, + Alignment.center, + ); + }); + + testWidgets('right 默认 drawer 宽度与 inset', (tester) async { + final layout = PopupLayout( + placement: TPopupPlacement.right, + inset: const TPopupRightInset(top: 8, bottom: 8), + ); + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: Stack( + children: [layout.wrapPositioned(child: const SizedBox())], + ), + ), + ), + ); + final positioned = tester.widget(find.byType(Positioned)); + expect(positioned.width, PopupLayout.defaultDrawerWidth); + expect(positioned.right, 0); + expect(positioned.top, 8); + }); + + testWidgets('left 默认 drawer 宽度', (tester) async { + final layout = PopupLayout( + placement: TPopupPlacement.left, + ); + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: Stack( + children: [layout.wrapPositioned(child: const SizedBox())], + ), + ), + ), + ); + expect( + tester.widget(find.byType(Positioned)).width, + PopupLayout.defaultDrawerWidth, + ); + }); + }); + + // ============================================================ + // TPopupOptions 命名工厂(B 方案):编译期挡误用 + 运行期与默认构造等价 + // ============================================================ + group('TPopupOptions 命名工厂', () { + test('.bottom 生成的 options 等价于默认构造(含默认 sentinel)', () { + final factoryOpts = TPopupOptions.bottom( + child: const SizedBox(), + height: 300, + ); + final baseOpts = TPopupOptions( + child: const SizedBox(), + placement: TPopupPlacement.bottom, + height: 300, + ); + expect(factoryOpts.placement, baseOpts.placement); + expect(factoryOpts.height, 300); + expect(factoryOpts.width, isNull); + expect(factoryOpts.usesDefaultHeader, isTrue); + expect(factoryOpts.usesDefaultCancel, isTrue); + expect(factoryOpts.usesDefaultConfirm, isTrue); + }); + + test('.center 生成 placement=center + 默认 closeBuilder', () { + final opts = TPopupOptions.center( + child: const SizedBox(), + width: 220, + height: 220, + ); + expect(opts.placement, TPopupPlacement.center); + expect(opts.width, 220); + expect(opts.height, 220); + expect(opts.usesDefaultClose, isTrue); + expect(opts.inset, isNull); + }); + + test('.top / .left / .right 生成对应 placement + 默认无头部', () { + final top = TPopupOptions.top(child: const SizedBox(), height: 100); + expect(top.placement, TPopupPlacement.top); + expect(top.height, 100); + expect(top.width, isNull); + + final left = TPopupOptions.left(child: const SizedBox(), width: 280); + expect(left.placement, TPopupPlacement.left); + expect(left.width, 280); + expect(left.height, isNull); + + final right = TPopupOptions.right(child: const SizedBox(), width: 280); + expect(right.placement, TPopupPlacement.right); + expect(right.width, 280); + expect(right.height, isNull); + }); + + test('factory 输出的合法配置在 assertPlacementParams 下零异常', () { + final variants = [ + TPopupOptions.bottom(child: const SizedBox(), height: 300), + TPopupOptions.center(child: const SizedBox(), width: 220, height: 220), + TPopupOptions.top(child: const SizedBox(), height: 100), + TPopupOptions.left(child: const SizedBox(), width: 280), + TPopupOptions.right(child: const SizedBox(), width: 280), + ]; + for (final opts in variants) { + expect(opts.assertPlacementParams, returnsNormally, + reason: 'placement=${opts.placement}'); + } + }); + + test('factory 透传通用字段:animationDuration / overlay / callbacks', () { + var visibleChanges = 0; + final opts = TPopupOptions.bottom( + child: const SizedBox(), + animationDuration: const Duration(milliseconds: 500), + showOverlay: false, + overlayOpacity: 0.5, + destroyOnClose: true, + onVisibleChange: (_, __) => visibleChanges++, + ); + expect(opts.animationDuration, const Duration(milliseconds: 500)); + expect(opts.showOverlay, isFalse); + expect(opts.closeOnOverlayClick, isFalse); + expect(opts.overlayOpacity, 0.5); + expect(opts.destroyOnClose, isTrue); + opts.onVisibleChange?.call(false, TPopupTrigger.api); + expect(visibleChanges, 1); + }); + }); + + // ============================================================ + // TPopupOptions.copyWith:处理 sentinel 三态 + // ============================================================ + group('TPopupOptions.copyWith', () { + test('不传字段 → 完全继承原值(含 sentinel 默认 builder)', () { + final base = TPopupOptions(child: const SizedBox()); + final next = base.copyWith(); + expect(next.placement, base.placement); + expect(next.height, base.height); + // sentinel 身份延续 + expect(next.usesDefaultHeader, isTrue); + expect(next.usesDefaultCancel, isTrue); + expect(next.usesDefaultConfirm, isTrue); + expect(next.usesDefaultClose, isTrue); + }); + + test('显式传 null → 把对应 builder 置为「隐藏」', () { + final base = TPopupOptions(child: const SizedBox()); + final next = base.copyWith(headerBuilder: null, cancelBuilder: null); + expect(next.headerBuilder, isNull); + expect(next.cancelBuilder, isNull); + // 未传的字段仍是 sentinel + expect(next.usesDefaultConfirm, isTrue); + expect(next.usesDefaultClose, isTrue); + }); + + test('传自定义 builder → 替换为自定义', () { + final base = TPopupOptions(child: const SizedBox()); + const titleWidget = Text('t'); + final next = base.copyWith(titleWidget: titleWidget); + expect(next.titleWidget, same(titleWidget)); + // 其它继承 + expect(next.usesDefaultHeader, isTrue); + }); + + test('值类字段(width / height / radius / 颜色 / Color?)三态正确', () { + final base = TPopupOptions( + child: const SizedBox(), + width: 100, + height: 200, + radius: 8, + backgroundColor: const Color(0xFF000000), + ); + // 不传:继承 + expect(base.copyWith().width, 100); + // 显式 null:清空 + expect(base.copyWith(width: null).width, isNull); + expect(base.copyWith(backgroundColor: null).backgroundColor, isNull); + // 替换 + expect(base.copyWith(width: 50).width, 50); + }); + + test('回调字段同样支持三态', () { + hookA(bool _, TPopupTrigger __) {} + final base = TPopupOptions( + child: const SizedBox(), + onVisibleChange: hookA, + ); + expect(base.copyWith().onVisibleChange, same(hookA)); + expect(base.copyWith(onVisibleChange: null).onVisibleChange, isNull); + }); + }); + + // ============================================================ + // TPopupHandle.open(): 缓存 navigator,无 context 再开 + // ============================================================ + group('TPopupHandle.open() 无参复用缓存 navigator', () { + testWidgets('close 后 open() 不传 context 仍可重新打开', (tester) async { + late TPopupHandle handle; + await openPopup( + tester, + onPressed: () { + handle = TPopup.show( + tester.element(find.text('open')), + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 160, + titleWidget: const Text('再开测试'), + child: const SizedBox(height: 60)), + ); + }, + ); + await tester.pumpAndSettle(); + expect(handle.isShowing, isTrue); + + handle.close(); + await tester.pumpAndSettle(); + expect(handle.isShowing, isFalse); + + // 关键:不传 context,使用首次缓存的 navigator + handle.open(); + await tester.pumpAndSettle(); + expect(handle.isShowing, isTrue); + expect(find.text('再开测试'), findsOneWidget); + }); + + testWidgets('已展示状态下 open() 重复调用无副作用', (tester) async { + late TPopupHandle handle; + await openPopup( + tester, + onPressed: () { + handle = TPopup.show( + tester.element(find.text('open')), + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 160, + child: const SizedBox(height: 60)), + ); + }, + ); + await tester.pumpAndSettle(); + handle.open(); // 无参数重复 + await tester.pumpAndSettle(); + expect(handle.isShowing, isTrue); + // 只有一个 popup route + expect(find.byType(SizedBox), findsWidgets); + }); + }); + + // ============================================================ + // Header 三态行为 + 触发源精细化(覆盖 _popup_header.dart 短路 fix) + // ============================================================ + group('Popup Header 三态完整对照', () { + /// 拿到 popup 内 PopupHeader 实际占用的高度。 + /// - 无头部(headerBuilder=null 或三槽全 null):== 0 + /// - 默认 sentinel header:== PopupHeader.headerHeight(58) + /// - 自定义 headerBuilder:取决于自定义 widget 的高度 + double popupHeaderHeight(WidgetTester tester) { + return tester.getSize(find.byType(PopupHeader)).height; + } + + testWidgets('headerBuilder=null 同时传 title/cancel/confirmBuilder 也不渲染', + (tester) async { + await openPopup( + tester, + onPressed: () { + TPopup.show( + tester.element(find.text('open')), + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 180, + headerBuilder: null, // 一票否决 + titleWidget: const Text('应隐藏-标题'), + cancelBuilder: (_, close) => + GestureDetector(onTap: close, child: const Text('应隐藏-左')), + confirmBuilder: (_, close) => + GestureDetector(onTap: close, child: const Text('应隐藏-右')), + child: + const SizedBox(key: ValueKey('popup-child'), height: 80)), + ); + }, + ); + await tester.pumpAndSettle(); + expect(find.text('应隐藏-标题'), findsNothing); + expect(find.text('应隐藏-左'), findsNothing); + expect(find.text('应隐藏-右'), findsNothing); + // 也不应该有内置文案 + expect(find.text('取消'), findsNothing); + expect(find.text('确定'), findsNothing); + }); + + testWidgets('headerBuilder=null → PopupHeader 高度精确为 0', (tester) async { + await openPopup( + tester, + onPressed: () { + TPopup.show( + tester.element(find.text('open')), + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 180, + headerBuilder: null, + child: const SizedBox(height: 80)), + ); + }, + ); + await tester.pumpAndSettle(); + expect(popupHeaderHeight(tester), 0); + }); + + testWidgets( + '默认 sentinel + 三槽全 null → PopupHeader 高度精确为 0(_popup_header.dart 修复回归)', + (tester) async { + await openPopup( + tester, + onPressed: () { + TPopup.show( + tester.element(find.text('open')), + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 180, + cancelBuilder: null, + confirmBuilder: null, + child: const SizedBox(height: 80)), + ); + }, + ); + await tester.pumpAndSettle(); + expect(popupHeaderHeight(tester), 0); + expect(find.text('取消'), findsNothing); + expect(find.text('确定'), findsNothing); + }); + + testWidgets('对照:默认 sentinel header(含标题)PopupHeader 高度 == 58', + (tester) async { + await openPopup( + tester, + onPressed: () { + TPopup.show( + tester.element(find.text('open')), + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 180, + titleWidget: const Text('标题'), + child: const SizedBox(height: 80)), + ); + }, + ); + await tester.pumpAndSettle(); + expect(popupHeaderHeight(tester), PopupHeader.headerHeight); + }); + + testWidgets('仅 cancelBuilder=null → 隐藏左槽,标题与右槽仍渲染', (tester) async { + await openPopup( + tester, + onPressed: () { + TPopup.show( + tester.element(find.text('open')), + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 180, + cancelBuilder: null, + titleWidget: const Text('标题在'), + child: const SizedBox(height: 80)), + ); + }, + ); + await tester.pumpAndSettle(); + expect(find.text('标题在'), findsOneWidget); + expect(find.text('取消'), findsNothing); + expect(find.text('确定'), findsOneWidget); + }); + + testWidgets('仅 confirmBuilder=null → 隐藏右槽,标题与左槽仍渲染', (tester) async { + await openPopup( + tester, + onPressed: () { + TPopup.show( + tester.element(find.text('open')), + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 180, + confirmBuilder: null, + titleWidget: const Text('标题在'), + child: const SizedBox(height: 80)), + ); + }, + ); + await tester.pumpAndSettle(); + expect(find.text('标题在'), findsOneWidget); + expect(find.text('取消'), findsOneWidget); + expect(find.text('确定'), findsNothing); + }); + + testWidgets( + 'headerBuilder 自定义 → titleWidget / cancelBuilder / confirmBuilder 全部被忽略', + (tester) async { + await openPopup( + tester, + onPressed: () { + TPopup.show( + tester.element(find.text('open')), + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 180, + headerBuilder: (_, __) => const Text('唯一头部'), + titleWidget: const Text('不该出现-标题'), + cancelBuilder: (_, __) => const Text('不该出现-左'), + confirmBuilder: (_, __) => const Text('不该出现-右'), + child: const SizedBox(height: 80)), + ); + }, + ); + await tester.pumpAndSettle(); + expect(find.text('唯一头部'), findsOneWidget); + expect(find.text('不该出现-标题'), findsNothing); + expect(find.text('不该出现-左'), findsNothing); + expect(find.text('不该出现-右'), findsNothing); + // 也找不到内置默认文案 + expect(find.text('取消'), findsNothing); + expect(find.text('确定'), findsNothing); + }); + }); + + // ============================================================ + // 触发源精细化:sentinel vs 自定义 builder 的 close 上报区分 + // ============================================================ + group('Popup 触发源细分(sentinel vs 自定义 builder)', () { + testWidgets('内置 cancel 按钮点击 → cancel(与 confirm 区分)', (tester) async { + TPopupTrigger? lastTrigger; + await openPopup( + tester, + onPressed: () { + TPopup.show( + tester.element(find.text('open')), + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 160, + onVisibleChange: (visible, trigger) { + if (!visible) { + lastTrigger = trigger; + } + }, + child: const SizedBox(height: 60)), + ); + }, + ); + await tester.pumpAndSettle(); + await tester.tap(find.text('取消')); + await tester.pumpAndSettle(); + expect(lastTrigger, TPopupTrigger.cancel); + }); + + testWidgets('自定义 cancelBuilder 内调 close → cancel', (tester) async { + TPopupTrigger? lastTrigger; + await openPopup( + tester, + onPressed: () { + TPopup.show( + tester.element(find.text('open')), + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 160, + cancelBuilder: (_, close) => GestureDetector( + onTap: close, + child: const Text('我自己的取消'), + ), + onVisibleChange: (visible, trigger) { + if (!visible) { + lastTrigger = trigger; + } + }, + child: const SizedBox(height: 60)), + ); + }, + ); + await tester.pumpAndSettle(); + await tester.tap(find.text('我自己的取消')); + await tester.pumpAndSettle(); + expect(lastTrigger, TPopupTrigger.cancel); + }); + + testWidgets('自定义 confirmBuilder 内调 close → confirm', (tester) async { + TPopupTrigger? lastTrigger; + await openPopup( + tester, + onPressed: () { + TPopup.show( + tester.element(find.text('open')), + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 160, + confirmBuilder: (_, close) => GestureDetector( + onTap: close, + child: const Text('我自己的确定'), + ), + onVisibleChange: (visible, trigger) { + if (!visible) { + lastTrigger = trigger; + } + }, + child: const SizedBox(height: 60)), + ); + }, + ); + await tester.pumpAndSettle(); + await tester.tap(find.text('我自己的确定')); + await tester.pumpAndSettle(); + expect(lastTrigger, TPopupTrigger.confirm); + }); + + testWidgets('center 自定义 closeBuilder 内调 close → close', (tester) async { + TPopupTrigger? lastTrigger; + await openPopup( + tester, + onPressed: () { + TPopup.show( + tester.element(find.text('open')), + options: TPopupOptions( + placement: TPopupPlacement.center, + width: 200, + height: 200, + closeBuilder: (_, close) => GestureDetector( + onTap: close, + child: const Text('我自己的关闭'), + ), + onVisibleChange: (visible, trigger) { + if (!visible) { + lastTrigger = trigger; + } + }, + child: const SizedBox(height: 80, width: 80)), + ); + }, + ); + await tester.pumpAndSettle(); + await tester.tap(find.text('我自己的关闭')); + await tester.pumpAndSettle(); + expect(lastTrigger, TPopupTrigger.close); + }); + + testWidgets('headerBuilder 自定义内调 close → custom(无预设动作语义)', (tester) async { + TPopupTrigger? lastTrigger; + await openPopup( + tester, + onPressed: () { + TPopup.show( + tester.element(find.text('open')), + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 160, + headerBuilder: (_, close) => GestureDetector( + onTap: close, + child: const Text('整行自定义头部'), + ), + onVisibleChange: (visible, trigger) { + if (!visible) { + lastTrigger = trigger; + } + }, + child: const SizedBox(height: 60)), + ); + }, + ); + await tester.pumpAndSettle(); + await tester.tap(find.text('整行自定义头部')); + await tester.pumpAndSettle(); + expect(lastTrigger, TPopupTrigger.custom); + }); + }); +} diff --git a/tdesign-component/test/t_popup_layout_test.dart b/tdesign-component/test/t_popup_layout_test.dart new file mode 100644 index 000000000..915520b70 --- /dev/null +++ b/tdesign-component/test/t_popup_layout_test.dart @@ -0,0 +1,208 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:tdesign_flutter/src/components/popup/t_popup.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +void main() { + group('PopupLayout', () { + testWidgets('top placement 使用 height 与左右 inset', (tester) async { + final layout = PopupLayout( + placement: TPopupPlacement.top, + inset: const TPopupTopInset(left: 4, right: 6), + height: 100, + ); + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: Stack(children: [ + layout.wrapPositioned(child: const SizedBox(height: 50)) + ]), + ), + ), + ); + final positioned = tester.widget(find.byType(Positioned)); + expect(positioned.top, 0); + expect(positioned.left, 4); + expect(positioned.right, 6); + expect(positioned.height, 100); + }); + + testWidgets('bottom placement 使用左右 inset 与固定 height', (tester) async { + final layout = PopupLayout( + placement: TPopupPlacement.bottom, + inset: const TPopupBottomInset(left: 12, right: 20), + height: 200, + ); + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: Stack(children: [ + layout.wrapPositioned(child: const SizedBox(height: 50)) + ]), + ), + ), + ); + final positioned = tester.widget(find.byType(Positioned)); + expect(positioned.left, 12); + expect(positioned.right, 20); + expect(positioned.bottom, 0); + expect(positioned.height, 200); + }); + + testWidgets('bottom 无 height 时贴底', (tester) async { + final layout = PopupLayout( + placement: TPopupPlacement.bottom, + ); + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: Stack( + children: [ + layout.wrapPositioned(child: const SizedBox(height: 1)) + ], + ), + ), + ), + ); + final positioned = tester.widget(find.byType(Positioned)); + expect(positioned.bottom, 0); + expect(positioned.top, isNull); + expect(positioned.height, isNull); + }); + + testWidgets('left / right 使用默认或自定义 width 与上下 inset', (tester) async { + final left = PopupLayout( + placement: TPopupPlacement.left, + inset: const TPopupLeftInset(top: 56, bottom: 12), + width: 300, + ); + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: + Stack(children: [left.wrapPositioned(child: const SizedBox())]), + ), + ), + ); + var positioned = tester.widget(find.byType(Positioned)); + expect(positioned.width, 300); + expect(positioned.top, 56); + expect(positioned.bottom, 12); + + final right = PopupLayout( + placement: TPopupPlacement.right, + inset: const TPopupRightInset(top: 8, bottom: 10), + width: 260, + ); + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: Stack( + children: [right.wrapPositioned(child: const SizedBox())], + ), + ), + ), + ); + positioned = tester.widget(find.byType(Positioned)); + expect(positioned.width, 260); + expect(positioned.top, 8); + expect(positioned.bottom, 10); + }); + + testWidgets('center placement 仅 Center 包裹(尺寸由 PopupShell 控制)', + (tester) async { + final layout = PopupLayout( + placement: TPopupPlacement.center, + width: 200, + height: 150, + ); + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: Stack( + children: [ + layout.wrapPositioned( + child: const SizedBox( + key: ValueKey('content'), + width: 200, + height: 150, + ), + ), + ], + ), + ), + ), + ); + expect(find.byType(Center), findsOneWidget); + final box = + tester.widget(find.byKey(const ValueKey('content'))); + expect(box.width, 200); + expect(box.height, 150); + }); + + test('slideOffset 五向偏移', () { + final layout = PopupLayout( + placement: TPopupPlacement.top, + ); + expect(layout.slideOffset(0), const Offset(0, -1)); + expect(layout.slideOffset(1), const Offset(0, 0)); + + final bottom = PopupLayout( + placement: TPopupPlacement.bottom, + ); + expect(bottom.slideOffset(0), const Offset(0, 1)); + + final left = PopupLayout( + placement: TPopupPlacement.left, + ); + expect(left.slideOffset(0.5), const Offset(-0.5, 0)); + + final right = PopupLayout( + placement: TPopupPlacement.right, + ); + expect(right.slideOffset(0.5), const Offset(0.5, 0)); + + final center = PopupLayout( + placement: TPopupPlacement.center, + ); + expect(center.slideOffset(0.5), Offset.zero); + }); + + testWidgets('center 仅 Positioned.fill + Center,由 PopupShell 控制尺寸', + (tester) async { + final layout = PopupLayout( + placement: TPopupPlacement.center, + width: 100, + height: 80, + ); + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: Stack( + children: [ + layout.wrapPositioned(child: const SizedBox(height: 200)), + ], + ), + ), + ), + ); + final center = tester.widget
(find.byType(Center)); + expect(center.child, isA()); + }); + + test('alignment 各方向', () { + expect( + PopupLayout( + placement: TPopupPlacement.top, + ).alignment, + Alignment.topCenter, + ); + expect( + PopupLayout( + placement: TPopupPlacement.right, + ).alignment, + Alignment.centerRight, + ); + }); + }); +} diff --git a/tdesign-component/test/t_popup_options_test.dart b/tdesign-component/test/t_popup_options_test.dart new file mode 100644 index 000000000..55c7d4982 --- /dev/null +++ b/tdesign-component/test/t_popup_options_test.dart @@ -0,0 +1,389 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +void main() { + group('TPopupOptions', () { + test('默认 placement 为 bottom,4 个 builder 默认 sentinel', () { + final options = TPopupOptions(child: const SizedBox()).normalized(); + expect(options.placement, TPopupPlacement.bottom); + expect(options.modal, isTrue); + expect(options.closeOnOverlayClick, isTrue); + expect(options.usesDefaultHeader, isTrue); + expect(options.usesDefaultCancel, isTrue); + expect(options.usesDefaultConfirm, isTrue); + expect(options.titleWidget, isNull); + }); + + test('showOverlay=false 且省略 closeOnOverlayClick 时默认按 false 解析', () { + final options = TPopupOptions( + child: const SizedBox(), + showOverlay: false, + ).normalized(); + expect(options.closeOnOverlayClick, isFalse); + }); + + test('模态与蒙层合法组合矩阵可通过校验并解析默认关闭行为', () { + void expectValid( + String reason, + TPopupOptions options, { + required bool expectedCloseOnOverlayClick, + }) { + expect( + () => options.assertPlacementParams(), + returnsNormally, + reason: reason, + ); + expect( + options.closeOnOverlayClick, + expectedCloseOnOverlayClick, + reason: reason, + ); + } + + expectValid( + '标准模态 + 默认蒙层关闭', + TPopupOptions( + child: const SizedBox(), + showOverlay: true, + modal: true, + ), + expectedCloseOnOverlayClick: true, + ); + expectValid( + '标准模态 + 显式禁止蒙层关闭', + TPopupOptions( + child: const SizedBox(), + showOverlay: true, + modal: true, + closeOnOverlayClick: false, + ), + expectedCloseOnOverlayClick: false, + ); + expectValid( + '透明模态 + 默认不允许蒙层关闭', + TPopupOptions( + child: const SizedBox(), + showOverlay: false, + modal: true, + ), + expectedCloseOnOverlayClick: false, + ); + expectValid( + '透明模态 + 显式 false 仍合法', + TPopupOptions( + child: const SizedBox(), + showOverlay: false, + modal: true, + closeOnOverlayClick: false, + ), + expectedCloseOnOverlayClick: false, + ); + expectValid( + '非模态浮层 + 默认不允许蒙层关闭', + TPopupOptions( + child: const SizedBox(), + showOverlay: false, + modal: false, + ), + expectedCloseOnOverlayClick: false, + ); + expectValid( + '非模态浮层 + 显式 false 仍合法', + TPopupOptions( + child: const SizedBox(), + showOverlay: false, + modal: false, + closeOnOverlayClick: false, + ), + expectedCloseOnOverlayClick: false, + ); + }); + + test('bottom 默认走内置三段式(useDefaultHeader)', () { + final options = TPopupOptions( + child: const SizedBox(), + placement: TPopupPlacement.bottom, + ).normalized(); + expect(options.useDefaultHeader, isTrue); + expect(options.useCustomHeader, isFalse); + expect(options.showCancelSlot, isTrue); + expect(options.showConfirmSlot, isTrue); + expect(options.hasBuiltInHeader, isTrue); + }); + + test('cancelBuilder / confirmBuilder 均为 null 时槽位隐藏', () { + final options = TPopupOptions( + child: const SizedBox(), + placement: TPopupPlacement.bottom, + cancelBuilder: null, + confirmBuilder: null, + ).normalized(); + expect(options.showCancelSlot, isFalse); + expect(options.showConfirmSlot, isFalse); + expect(options.hasBuiltInHeader, isFalse); // titleWidget 也为 null + }); + + test('headerBuilder null 不显示头部', () { + final options = TPopupOptions( + child: const SizedBox(), + placement: TPopupPlacement.bottom, + headerBuilder: null, + ).normalized(); + expect(options.useDefaultHeader, isFalse); + expect(options.useCustomHeader, isFalse); + expect(options.hasBuiltInHeader, isFalse); + }); + + test('headerBuilder 自定义 → useCustomHeader 为 true', () { + final options = TPopupOptions( + child: const SizedBox(), + placement: TPopupPlacement.bottom, + headerBuilder: (_, __) => const SizedBox(), + ).normalized(); + expect(options.useCustomHeader, isTrue); + expect(options.hasBuiltInHeader, isTrue); + }); + + test('normalized 忽略 bottom 的 closeBuilder(非 sentinel 也置 null)', () { + final options = TPopupOptions( + child: const SizedBox(), + placement: TPopupPlacement.bottom, + closeBuilder: (_, __) => const Text('x'), + ).normalized(); + expect(options.closeBuilder, isNull); + }); + + test('center 默认 closeBuilder 为 sentinel(内置图标)', () { + final options = TPopupOptions( + child: const SizedBox(), + placement: TPopupPlacement.center, + ).normalized(); + expect(options.usesDefaultClose, isTrue); + }); + + test('center closeBuilder=null 不显示关闭区', () { + final options = TPopupOptions( + child: const SizedBox(), + placement: TPopupPlacement.center, + closeBuilder: null, + ).normalized(); + expect(options.closeBuilder, isNull); + }); + + test('top 剥离 header 与三槽,重置为 null', () { + final options = TPopupOptions( + child: const SizedBox(), + placement: TPopupPlacement.top, + titleWidget: const Text('x'), + headerBuilder: (_, __) => const Text('h'), + ).normalized(); + expect(options.headerBuilder, isNull); + expect(options.titleWidget, isNull); + expect(options.cancelBuilder, isNull); + expect(options.confirmBuilder, isNull); + expect(options.hasBuiltInHeader, isFalse); + }); + + test('left/right 剥离 closeBuilder', () { + final left = TPopupOptions( + child: const SizedBox(), + placement: TPopupPlacement.left, + closeBuilder: (_, __) => const Text('x'), + ).normalized(); + expect(left.closeBuilder, isNull); + }); + + test('hasBuiltInHeader 内置三段中任一槽非 null 即 true', () { + // titleWidget 单独存在 + expect( + TPopupOptions( + child: const SizedBox(), + placement: TPopupPlacement.bottom, + titleWidget: const Text('x'), + cancelBuilder: null, + confirmBuilder: null, + ).normalized().hasBuiltInHeader, + isTrue, + ); + // 仅 cancel 默认(其它 null) + expect( + TPopupOptions( + child: const SizedBox(), + placement: TPopupPlacement.bottom, + confirmBuilder: null, + ).normalized().hasBuiltInHeader, + isTrue, + ); + // 完全无头部 + expect( + TPopupOptions( + child: const SizedBox(), + placement: TPopupPlacement.bottom, + headerBuilder: null, + ).normalized().hasBuiltInHeader, + isFalse, + ); + // 非 bottom 永远 false + expect( + TPopupOptions( + child: const SizedBox(), + placement: TPopupPlacement.top, + ).normalized().hasBuiltInHeader, + isFalse, + ); + }); + + test('assertPlacementParams debug 期对错位字段抛 FlutterError', () { + // left 不该有 height + expect( + () => TPopupOptions( + child: const SizedBox(), + placement: TPopupPlacement.left, + height: 100, + ).assertPlacementParams(), + throwsA(isA()), + ); + // center 不该有 titleWidget(属于 bottom 头部字段) + expect( + () => TPopupOptions( + child: const SizedBox(), + placement: TPopupPlacement.center, + titleWidget: const Text('x'), + ).assertPlacementParams(), + throwsA(isA()), + ); + expect( + () => TPopupOptions( + child: const SizedBox(), + showOverlay: true, + modal: false, + ).assertPlacementParams(), + throwsA(isA()), + ); + expect( + () => TPopupOptions( + child: const SizedBox(), + showOverlay: false, + closeOnOverlayClick: true, + ).assertPlacementParams(), + throwsA(isA()), + ); + }); + + test('模态与蒙层非法组合矩阵全部抛 FlutterError', () { + void expectInvalid(String reason, TPopupOptions options) { + expect( + () => options.assertPlacementParams(), + throwsA(isA()), + reason: reason, + ); + } + + expectInvalid( + '有蒙层但非模态(默认蒙层关闭值)', + TPopupOptions( + child: const SizedBox(), + showOverlay: true, + modal: false, + ), + ); + expectInvalid( + '有蒙层但非模态(显式 false)', + TPopupOptions( + child: const SizedBox(), + showOverlay: true, + modal: false, + closeOnOverlayClick: false, + ), + ); + expectInvalid( + '透明模态下显式要求蒙层关闭', + TPopupOptions( + child: const SizedBox(), + showOverlay: false, + modal: true, + closeOnOverlayClick: true, + ), + ); + expectInvalid( + '非模态浮层下显式要求蒙层关闭', + TPopupOptions( + child: const SizedBox(), + showOverlay: false, + modal: false, + closeOnOverlayClick: true, + ), + ); + }); + + test('assertPlacementParams 各 placement 的 inset 类型错位也抛错', () { + expect( + () => TPopupOptions( + child: const SizedBox(), + placement: TPopupPlacement.top, + inset: const TPopupBottomInset(left: 10), + ).assertPlacementParams(), + throwsA(isA()), + ); + expect( + () => TPopupOptions( + child: const SizedBox(), + placement: TPopupPlacement.right, + inset: const TPopupLeftInset(top: 10), + ).assertPlacementParams(), + throwsA(isA()), + ); + expect( + () => TPopupOptions( + child: const SizedBox(), + placement: TPopupPlacement.center, + inset: const TPopupTopInset(left: 10), + ).assertPlacementParams(), + throwsA(isA()), + ); + }); + + test('assertPlacementParams 合法配置不抛错', () { + // 各 placement 用对应合法字段 + expect( + () => TPopupOptions(child: const SizedBox()).assertPlacementParams(), + returnsNormally); + expect( + () => TPopupOptions( + child: const SizedBox(), + placement: TPopupPlacement.center, + width: 200, + height: 200, + ).assertPlacementParams(), + returnsNormally, + ); + expect( + () => TPopupOptions( + child: const SizedBox(), + placement: TPopupPlacement.left, + width: 280, + inset: const TPopupLeftInset(top: 10, bottom: 10), + ).assertPlacementParams(), + returnsNormally, + ); + expect( + () => TPopupOptions( + child: const SizedBox(), + showOverlay: false, + modal: false, + ).assertPlacementParams(), + returnsNormally, + ); + }); + + test('copyWith(closeOnOverlayClick: null) 可恢复为跟随 showOverlay 的默认值', () { + final options = TPopupOptions( + child: const SizedBox(), + showOverlay: false, + closeOnOverlayClick: false, + ).copyWith(closeOnOverlayClick: null); + expect(options.closeOnOverlayClick, isFalse); + }); + }); +} diff --git a/tdesign-component/test/t_popup_route_test.dart b/tdesign-component/test/t_popup_route_test.dart new file mode 100644 index 000000000..93253f90b --- /dev/null +++ b/tdesign-component/test/t_popup_route_test.dart @@ -0,0 +1,177 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import 'helpers/popup_test_helpers.dart'; +import 'helpers/popup_test_resource.dart'; + +void main() { + tearDown(resetPopupTestResource); + + group('Popup 路由层行为(通过 TPopup.show 验证)', () { + testWidgets('无蒙层 + modal=true 时阻断底层点击与滚动', + (tester) async { + final controller = ScrollController(); + var backgroundTapCount = 0; + + await tester.pumpWidget( + MaterialApp( + home: TTheme( + data: TThemeData.defaultData(), + child: Scaffold( + body: Builder( + builder: (context) { + return Column( + children: [ + ElevatedButton( + onPressed: () => backgroundTapCount++, + child: const Text('background button'), + ), + Expanded( + child: ListView.builder( + controller: controller, + itemCount: 30, + itemBuilder: (_, index) => SizedBox( + height: 48, + child: Text('item-$index'), + ), + ), + ), + ElevatedButton( + onPressed: () { + TPopup.show( + context, + options: TPopupOptions.bottom( + height: 120, + showOverlay: false, + modal: true, + cancelBuilder: null, + confirmBuilder: null, + child: const SizedBox(height: 60), + ), + ); + }, + child: const Text('open popup'), + ), + ], + ); + }, + ), + ), + ), + ), + ); + await tester.pumpAndSettle(); + + final backgroundButtonCenter = + tester.getCenter(find.text('background button')); + final listDragStart = tester.getCenter(find.text('item-3')); + + await tester.tap(find.text('open popup')); + await tester.pumpAndSettle(); + + await tester.tapAt(backgroundButtonCenter); + await tester.pump(); + expect(backgroundTapCount, 0); + + await tester.dragFrom(listDragStart, const Offset(0, -200)); + await tester.pump(); + expect(controller.offset, 0); + }); + + testWidgets('无蒙层 + modal=false 时允许底层点击与滚动', + (tester) async { + final controller = ScrollController(); + var backgroundTapCount = 0; + + await tester.pumpWidget( + MaterialApp( + home: TTheme( + data: TThemeData.defaultData(), + child: Scaffold( + body: Builder( + builder: (context) { + return Column( + children: [ + ElevatedButton( + onPressed: () => backgroundTapCount++, + child: const Text('background button'), + ), + Expanded( + child: ListView.builder( + controller: controller, + itemCount: 30, + itemBuilder: (_, index) => SizedBox( + height: 48, + child: Text('item-$index'), + ), + ), + ), + ElevatedButton( + onPressed: () { + TPopup.show( + context, + options: TPopupOptions.bottom( + height: 120, + showOverlay: false, + modal: false, + cancelBuilder: null, + confirmBuilder: null, + child: const SizedBox(height: 60), + ), + ); + }, + child: const Text('open popup'), + ), + ], + ); + }, + ), + ), + ), + ), + ); + await tester.pumpAndSettle(); + + final backgroundButtonCenter = + tester.getCenter(find.text('background button')); + final listDragStart = tester.getCenter(find.text('item-3')); + + await tester.tap(find.text('open popup')); + await tester.pumpAndSettle(); + + await tester.tapAt(backgroundButtonCenter); + await tester.pump(); + expect(backgroundTapCount, 1); + + await tester.dragFrom(listDragStart, const Offset(0, -200)); + await tester.pump(); + expect(controller.offset, greaterThan(0)); + }); + + testWidgets('fireCloseStart 仅触发一次 onClose', (tester) async { + var closeCount = 0; + late BuildContext hostContext; + TPopupHandle? handle; + + await openPopup( + tester, + onPressed: () { + hostContext = tester.element(find.text('open')); + handle = TPopup.show( + hostContext, + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 100, + onClose: () => closeCount++, + child: const SizedBox(height: 60)), + ); + }, + ); + await tester.pumpAndSettle(); + handle!.close(); + await tester.pumpAndSettle(); + expect(closeCount, 1); + }); + }); +} diff --git a/tdesign-component/test/t_popup_test.dart b/tdesign-component/test/t_popup_test.dart new file mode 100644 index 000000000..1191a3c03 --- /dev/null +++ b/tdesign-component/test/t_popup_test.dart @@ -0,0 +1,1507 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import 'helpers/popup_test_helpers.dart'; +import 'helpers/popup_test_resource.dart'; + +class _RecordingNavigatorObserver extends NavigatorObserver { + final List> pushedRoutes = >[]; + + @override + void didPush(Route route, Route? previousRoute) { + pushedRoutes.add(route); + super.didPush(route, previousRoute); + } + + int get popupPushCount => + pushedRoutes.where((route) => route is PopupRoute).length; +} + +void main() { + tearDown(resetPopupTestResource); + + group('TPopup 国际化文案', () { + for (final resource in [ + PopupTestResourceDelegate.zh(), + PopupTestResourceDelegate.en(), + ]) { + testWidgets( + '${resource.locale.languageCode} 底部默认 cancel / confirm 按钮点击关闭浮层', + (tester) async { + late BuildContext hostContext; + + await openPopup( + tester, + resource: resource, + onPressed: () { + hostContext = tester.element(find.text('open')); + TPopup.show( + hostContext, + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 200, + child: const SizedBox(height: 80)), + ); + }, + ); + await tester.pumpAndSettle(); + expect(find.text(resource.cancelText), findsOneWidget); + expect(find.text(resource.confirmText), findsOneWidget); + + await tester.tap(find.text(resource.cancelText)); + await tester.pumpAndSettle(); + expect(find.text(resource.cancelText), findsNothing); + + await openPopup( + tester, + resource: resource, + onPressed: () { + TPopup.show( + hostContext, + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 200, + child: const SizedBox(height: 80)), + ); + }, + ); + await tester.pumpAndSettle(); + await tester.tap(find.text(resource.confirmText)); + await tester.pumpAndSettle(); + expect(find.text(resource.confirmText), findsNothing); + }, + ); + } + + testWidgets('同一用例内切换中英文资源', (tester) async { + late BuildContext hostContext; + + for (final resource in [ + PopupTestResourceDelegate.zh(), + PopupTestResourceDelegate.en(), + ]) { + TPopupHandle? handle; + await openPopup( + tester, + resource: resource, + onPressed: () { + hostContext = tester.element(find.text('open')); + handle = TPopup.show( + hostContext, + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 160, + child: const SizedBox(height: 60)), + ); + }, + ); + await tester.pumpAndSettle(); + expect(find.text(resource.cancelText), findsOneWidget); + expect(find.text(resource.confirmText), findsOneWidget); + handle!.close(); + await tester.pumpAndSettle(); + } + }); + }); + + group('TPopup 生命周期', () { + testWidgets('show 触发 onOpen / onOpened 各一次', (tester) async { + var openCount = 0; + var openedCount = 0; + late BuildContext hostContext; + TPopupHandle? handle; + + await openPopup( + tester, + onPressed: () { + hostContext = tester.element(find.text('open')); + handle = TPopup.show( + hostContext, + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 120, + onOpen: () => openCount++, + onOpened: () => openedCount++, + child: const SizedBox(height: 80)), + ); + }, + ); + await tester.pump(); + expect(openCount, 1); + await tester.pumpAndSettle(); + expect(openedCount, 1); + + handle!.close(); + await tester.pumpAndSettle(); + }); + + testWidgets('handle.close 触发 onClose / onClosed', (tester) async { + var closeCount = 0; + var closedCount = 0; + var visibleChanges = []; + late BuildContext hostContext; + TPopupHandle? handle; + + await openPopup( + tester, + onPressed: () { + hostContext = tester.element(find.text('open')); + handle = TPopup.show( + hostContext, + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 120, + onClose: () => closeCount++, + onClosed: () => closedCount++, + onVisibleChange: (v, _) => visibleChanges.add(v), + child: const SizedBox(height: 80)), + ); + }, + ); + await tester.pumpAndSettle(); + handle!.close(); + await tester.pumpAndSettle(); + expect(closeCount, 1); + expect(closedCount, 1); + expect(visibleChanges, [true, false]); + expect(handle!.isShowing, false); + }); + + testWidgets('重复 close 不会重复触发 onClose', (tester) async { + var closeCount = 0; + late BuildContext hostContext; + TPopupHandle? handle; + + await openPopup( + tester, + onPressed: () { + hostContext = tester.element(find.text('open')); + handle = TPopup.show( + hostContext, + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 100, + onClose: () => closeCount++, + child: const SizedBox(height: 60)), + ); + }, + ); + await tester.pumpAndSettle(); + handle!.close(); + handle!.close(); + await tester.pumpAndSettle(); + expect(closeCount, 1); + }); + }); + + group('TPopup Navigator 选择', () { + testWidgets('navigatorContext 首次 show 挂载到指定 Navigator,且 handle.open 复用缓存', + (tester) async { + final rootObserver = _RecordingNavigatorObserver(); + final nestedObserver = _RecordingNavigatorObserver(); + late BuildContext rootContext; + late BuildContext nestedContext; + late TPopupHandle handle; + + await tester.pumpWidget( + MaterialApp( + navigatorObservers: [rootObserver], + home: TTheme( + data: TThemeData.defaultData(), + child: Builder( + builder: (rootCtx) { + rootContext = rootCtx; + return Navigator( + observers: [nestedObserver], + onGenerateRoute: (_) { + return MaterialPageRoute( + builder: (nestedCtx) { + nestedContext = nestedCtx; + return Scaffold( + body: ElevatedButton( + onPressed: () { + handle = TPopup.show( + nestedContext, + navigatorContext: rootContext, + options: TPopupOptions.bottom( + height: 120, + cancelBuilder: null, + confirmBuilder: null, + child: const SizedBox( + height: 60, + child: Text('root popup'), + ), + ), + ); + }, + child: const Text('open via root'), + ), + ); + }, + ); + }, + ); + }, + ), + ), + ), + ); + await tester.pumpAndSettle(); + + await tester.tap(find.text('open via root')); + await tester.pumpAndSettle(); + expect(rootObserver.popupPushCount, 1); + expect(nestedObserver.popupPushCount, 0); + expect(find.text('root popup'), findsOneWidget); + + handle.close(); + await tester.pumpAndSettle(); + expect(find.text('root popup'), findsNothing); + + handle.open(); + await tester.pumpAndSettle(); + expect(rootObserver.popupPushCount, 2); + expect(nestedObserver.popupPushCount, 0); + expect(find.text('root popup'), findsOneWidget); + }); + + testWidgets('useRootNavigator=true 会挂载到根 Navigator', (tester) async { + final rootObserver = _RecordingNavigatorObserver(); + final nestedObserver = _RecordingNavigatorObserver(); + late BuildContext nestedContext; + + await tester.pumpWidget( + MaterialApp( + navigatorObservers: [rootObserver], + home: TTheme( + data: TThemeData.defaultData(), + child: Navigator( + observers: [nestedObserver], + onGenerateRoute: (_) { + return MaterialPageRoute( + builder: (ctx) { + nestedContext = ctx; + return Scaffold( + body: ElevatedButton( + onPressed: () { + TPopup.show( + nestedContext, + useRootNavigator: true, + options: TPopupOptions.center( + width: 120, + height: 80, + closeBuilder: null, + child: const SizedBox( + width: 120, + height: 80, + child: Text('root navigator popup'), + ), + ), + ); + }, + child: const Text('open root navigator'), + ), + ); + }, + ); + }, + ), + ), + ), + ); + await tester.pumpAndSettle(); + + await tester.tap(find.text('open root navigator')); + await tester.pumpAndSettle(); + expect(rootObserver.popupPushCount, 1); + expect(nestedObserver.popupPushCount, 0); + expect(find.text('root navigator popup'), findsOneWidget); + }); + }); + + group('TPopup placement', () { + const placements = [ + TPopupPlacement.top, + TPopupPlacement.bottom, + TPopupPlacement.left, + TPopupPlacement.right, + TPopupPlacement.center, + ]; + for (final placement in placements) { + testWidgets('$placement 可正常打开关闭', (tester) async { + late BuildContext hostContext; + TPopupHandle? handle; + await openPopup( + tester, + onPressed: () { + hostContext = tester.element(find.text('open')); + handle = TPopup.show( + hostContext, + options: TPopupOptions( + placement: placement, + height: placement == TPopupPlacement.left || + placement == TPopupPlacement.right + ? null + : 120, + width: placement == TPopupPlacement.top || + placement == TPopupPlacement.bottom + ? null + : 200, + child: const SizedBox(height: 60, width: 60)), + ); + }, + ); + await tester.pumpAndSettle(); + expect(find.byType(Stack), findsWidgets); + handle!.close(); + await tester.pumpAndSettle(); + }); + } + }); + + group('TPopup Header', () { + testWidgets('底部默认头部:title / cancel / confirm 按钮渲染', (tester) async { + late BuildContext hostContext; + + await openPopup( + tester, + onPressed: () { + hostContext = tester.element(find.text('open')); + TPopup.show( + hostContext, + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 200, + titleWidget: TText('标题'), + child: const SizedBox(height: 80)), + ); + }, + ); + await tester.pumpAndSettle(); + expect(find.text('标题'), findsOneWidget); + expect(find.text('取消'), findsOneWidget); + expect(find.text('确定'), findsOneWidget); + + // 点取消默认 sentinel 自带 close → 浮层关闭 + await tester.tap(find.text('取消')); + await tester.pumpAndSettle(); + expect(find.text('标题'), findsNothing); + + // 重新打开点确定 → 同样关闭 + await openPopup( + tester, + onPressed: () { + TPopup.show( + hostContext, + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 200, + child: const SizedBox(height: 80)), + ); + }, + ); + await tester.pumpAndSettle(); + await tester.tap(find.text('确定')); + await tester.pumpAndSettle(); + expect(find.text('确定'), findsNothing); + }); + + testWidgets('cancelBuilder 自定义可选择不调 close → 浮层保持展示', (tester) async { + await openPopup( + tester, + onPressed: () { + TPopup.show( + tester.element(find.text('open')), + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 200, + cancelBuilder: (_, __) => GestureDetector( + onTap: () {}, // 不调 close + child: const Text('自定义取消'), + ), + child: const SizedBox(height: 80)), + ); + }, + ); + await tester.pumpAndSettle(); + await tester.tap(find.text('自定义取消')); + await tester.pump(); + expect(find.text('自定义取消'), findsOneWidget); + }); + + testWidgets('bottom headerBuilder=null 不渲染头部', (tester) async { + await openPopup( + tester, + onPressed: () { + TPopup.show( + tester.element(find.text('open')), + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 180, + titleWidget: TText('不应出现'), + headerBuilder: null, + child: const SizedBox(height: 80)), + ); + }, + ); + await tester.pumpAndSettle(); + expect(find.text('不应出现'), findsNothing); + expect(find.text('取消'), findsNothing); + }); + + testWidgets('bottom 仅标题无操作栏', (tester) async { + await openPopup( + tester, + onPressed: () { + TPopup.show( + tester.element(find.text('open')), + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 180, + titleWidget: TText('仅标题'), + cancelBuilder: null, + confirmBuilder: null, + child: const SizedBox(height: 80)), + ); + }, + ); + await tester.pumpAndSettle(); + expect(find.text('仅标题'), findsOneWidget); + expect(find.text('取消'), findsNothing); + expect(find.text('确定'), findsNothing); + expect(find.byIcon(TIcons.close), findsNothing); + }); + + testWidgets('headerBuilder 自定义头部', (tester) async { + await openPopup( + tester, + onPressed: () { + TPopup.show( + tester.element(find.text('open')), + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 160, + headerBuilder: (_, __) => const Text('自定义:传入标题'), + child: const SizedBox(height: 60)), + ); + }, + ); + await tester.pumpAndSettle(); + expect(find.textContaining('自定义'), findsOneWidget); + }); + + testWidgets('居中关闭在内容与下方', (tester) async { + late BuildContext hostContext; + + await openPopup( + tester, + onPressed: () { + hostContext = tester.element(find.text('open')); + TPopup.show( + hostContext, + options: TPopupOptions( + placement: TPopupPlacement.center, + width: 120, + height: 120, + child: const SizedBox(height: 80, width: 80)), + ); + }, + ); + await tester.pumpAndSettle(); + expect(find.byIcon(TIcons.close_circle), findsOneWidget); + await tester.tap(find.byIcon(TIcons.close_circle)); + await tester.pumpAndSettle(); + }); + + testWidgets('居中关闭在下方与示例一致 expand 不报错', (tester) async { + await openPopup( + tester, + onPressed: () { + TPopup.show( + tester.element(find.text('open')), + options: TPopupOptions( + placement: TPopupPlacement.center, + width: 240, + height: 240, + child: const SizedBox.expand()), + ); + }, + ); + await tester.pumpAndSettle(); + expect(find.byIcon(TIcons.close_circle), findsOneWidget); + }); + + testWidgets('center closeBuilder=null 不显示关闭区', (tester) async { + await openPopup( + tester, + onPressed: () { + TPopup.show( + tester.element(find.text('open')), + options: TPopupOptions( + placement: TPopupPlacement.center, + width: 120, + height: 120, + closeBuilder: null, + child: const SizedBox(height: 80, width: 80)), + ); + }, + ); + await tester.pumpAndSettle(); + expect(find.byIcon(TIcons.close_circle), findsNothing); + }); + + testWidgets('cancelBuilder / confirmBuilder', (tester) async { + await openPopup( + tester, + onPressed: () { + TPopup.show( + tester.element(find.text('open')), + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 160, + cancelBuilder: (_, __) => const Text('自定义取消'), + confirmBuilder: (_, __) => const Text('自定义确认'), + child: const SizedBox(height: 60)), + ); + }, + ); + await tester.pumpAndSettle(); + expect(find.text('自定义取消'), findsOneWidget); + expect(find.text('自定义确认'), findsOneWidget); + }); + }); + + group('TPopup 蒙层与行为', () { + testWidgets('点击蒙层关闭', (tester) async { + var overlayClose = 0; + late BuildContext hostContext; + + await openPopup( + tester, + onPressed: () { + hostContext = tester.element(find.text('open')); + TPopup.show( + hostContext, + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 100, + onClosed: () => overlayClose++, + child: const SizedBox(height: 60)), + ); + }, + ); + await tester.pumpAndSettle(); + // 点击蒙层区域(屏幕上方) + await tester.tapAt(const Offset(10, 10)); + await tester.pumpAndSettle(); + expect(overlayClose, 1); + }); + + testWidgets('closeOnOverlayClick 为 false 点击蒙层不关闭', (tester) async { + late BuildContext hostContext; + TPopupHandle? handle; + await openPopup( + tester, + onPressed: () { + hostContext = tester.element(find.text('open')); + handle = TPopup.show( + hostContext, + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 100, + closeOnOverlayClick: false, + onOverlayClick: () {}, + child: const SizedBox(height: 60)), + ); + }, + ); + await tester.pumpAndSettle(); + expect(find.text('取消'), findsOneWidget); + await tester.tapAt(const Offset(10, 10)); + await tester.pump(); + expect(find.text('取消'), findsOneWidget); + handle!.close(); + await tester.pumpAndSettle(); + }); + + testWidgets('showOverlay false 且 modal=true', (tester) async { + await openPopup( + tester, + onPressed: () { + TPopup.show( + tester.element(find.text('open')), + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 100, + showOverlay: false, + modal: true, + child: const SizedBox(height: 60)), + ); + }, + ); + await tester.pumpAndSettle(); + expect( + find.byType(ModalBarrier), + findsWidgets, + ); + }); + + testWidgets('模态与蒙层合法组合矩阵都可正常 show / close', (tester) async { + late BuildContext hostContext; + + await tester.pumpWidget( + wrapPopupTest( + Builder( + builder: (context) { + hostContext = context; + return const SizedBox.shrink(); + }, + ), + ), + ); + + Future expectShowAndClose( + String label, + TPopupOptions options, + ) async { + late TPopupHandle handle; + expect( + () => handle = TPopup.show(hostContext, options: options), + returnsNormally, + reason: label, + ); + await tester.pumpAndSettle(); + expect(find.text(label), findsOneWidget, reason: label); + + handle.close(); + await tester.pumpAndSettle(); + expect(find.text(label), findsNothing, reason: label); + } + + await expectShowAndClose( + '标准模态默认关闭', + TPopupOptions.bottom( + height: 100, + showOverlay: true, + modal: true, + cancelBuilder: null, + confirmBuilder: null, + child: const SizedBox(height: 60, child: Text('标准模态默认关闭')), + ), + ); + await expectShowAndClose( + '标准模态显式禁止蒙层关闭', + TPopupOptions.bottom( + height: 100, + showOverlay: true, + modal: true, + closeOnOverlayClick: false, + cancelBuilder: null, + confirmBuilder: null, + child: const SizedBox(height: 60, child: Text('标准模态显式禁止蒙层关闭')), + ), + ); + await expectShowAndClose( + '透明模态默认关闭策略', + TPopupOptions.bottom( + height: 100, + showOverlay: false, + modal: true, + cancelBuilder: null, + confirmBuilder: null, + child: const SizedBox(height: 60, child: Text('透明模态默认关闭策略')), + ), + ); + await expectShowAndClose( + '非模态浮层默认关闭策略', + TPopupOptions.bottom( + height: 100, + showOverlay: false, + modal: false, + cancelBuilder: null, + confirmBuilder: null, + child: const SizedBox(height: 60, child: Text('非模态浮层默认关闭策略')), + ), + ); + }); + + testWidgets('overlayOpacity 与自定义颜色', (tester) async { + await openPopup( + tester, + onPressed: () { + TPopup.show( + tester.element(find.text('open')), + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 80, + overlayColor: Colors.red, + overlayOpacity: 0.5, + child: const SizedBox(height: 40)), + ); + }, + ); + await tester.pumpAndSettle(); + await tester.pump(); + }); + + testWidgets('right inset.top 可控制顶部留白', (tester) async { + await openPopup( + tester, + onPressed: () { + TPopup.show( + tester.element(find.text('open')), + options: TPopupOptions( + placement: TPopupPlacement.right, + inset: const TPopupRightInset(top: 80), + child: const SizedBox(height: 200)), + ); + }, + ); + await tester.pumpAndSettle(); + }); + }); + + group('TPopupHandle / Tracker', () { + testWidgets('重复 show 可叠加打开(返回不同 handle)', (tester) async { + TPopupHandle? first; + TPopupHandle? second; + await openPopup( + tester, + onPressed: () { + final ctx = tester.element(find.text('open')); + first = TPopup.show( + ctx, + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 80, + cancelBuilder: null, + confirmBuilder: null, + child: const SizedBox(height: 40, child: Text('first'))), + ); + second = TPopup.show( + ctx, + options: TPopupOptions( + placement: TPopupPlacement.center, + width: 120, + height: 80, + closeBuilder: null, + child: const SizedBox(width: 120, height: 80, child: Text('second'))), + ); + expect(second!.isShowing, isTrue); + expect(identical(first, second), isFalse); + }, + ); + await tester.pumpAndSettle(); + expect(first!.isShowing, isTrue); + expect(second!.isShowing, isTrue); + expect(find.text('first'), findsOneWidget); + expect(find.text('second'), findsOneWidget); + + second!.close(); + await tester.pumpAndSettle(); + expect(second!.isShowing, isFalse); + expect(first!.isShowing, isTrue); + expect(find.text('second'), findsNothing); + expect(find.text('first'), findsOneWidget); + + first!.close(); + await tester.pumpAndSettle(); + }); + + testWidgets('外层 handle.close 在内层展示时只关闭外层', (tester) async { + TPopupHandle? outerHandle; + TPopupHandle? innerHandle; + + await openPopup( + tester, + onPressed: () { + outerHandle = TPopup.show( + tester.element(find.text('open')), + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 160, + cancelBuilder: null, + confirmBuilder: null, + child: Builder( + builder: (ctx) { + return Column( + children: [ + const Text('outer'), + ElevatedButton( + onPressed: () { + innerHandle = TPopup.show( + ctx, + options: TPopupOptions( + placement: TPopupPlacement.center, + width: 120, + height: 80, + closeBuilder: null, + child: const SizedBox( + width: 120, + height: 80, + child: Text('inner'), + ), + ), + ); + }, + child: const Text('open inner'), + ), + ], + ); + }, + )), + ); + }, + ); + await tester.pumpAndSettle(); + expect(find.text('outer'), findsOneWidget); + + await tester.tap(find.text('open inner')); + await tester.pumpAndSettle(); + expect(find.text('outer'), findsOneWidget); + expect(find.text('inner'), findsOneWidget); + + outerHandle!.close(); + await tester.pumpAndSettle(); + expect(outerHandle!.isShowing, isFalse); + expect(innerHandle!.isShowing, isTrue); + expect(find.text('outer'), findsNothing); + expect(find.text('inner'), findsOneWidget); + + innerHandle!.close(); + await tester.pumpAndSettle(); + expect(find.text('inner'), findsNothing); + }); + + testWidgets('系统返回键关闭并上报 systemBack trigger', (tester) async { + var closedCount = 0; + TPopupTrigger? hideTrigger; + late BuildContext hostContext; + + await openPopup( + tester, + onPressed: () { + hostContext = tester.element(find.text('open')); + TPopup.show( + hostContext, + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 100, + onClosed: () => closedCount++, + onVisibleChange: (visible, trigger) { + if (!visible) { + hideTrigger = trigger; + } + }, + child: const SizedBox(height: 60)), + ); + }, + ); + await tester.pumpAndSettle(); + await tester.binding.handlePopRoute(); + await tester.pumpAndSettle(); + expect(closedCount, 1); + expect(hideTrigger, TPopupTrigger.systemBack); + }); + + testWidgets('handle.close 后 handle.open 可再次展示', (tester) async { + late BuildContext hostContext; + TPopupHandle? handle; + + await openPopup( + tester, + onPressed: () { + hostContext = tester.element(find.text('open')); + handle = TPopup.show( + hostContext, + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 100, + cancelBuilder: null, + confirmBuilder: null, + child: const SizedBox(height: 60, child: Text('panel'))), + ); + }, + ); + await tester.pumpAndSettle(); + expect(find.text('panel'), findsOneWidget); + + handle!.close(); + await tester.pumpAndSettle(); + expect(handle!.isShowing, isFalse); + expect(find.text('panel'), findsNothing); + + handle!.open(hostContext); + await tester.pumpAndSettle(); + expect(handle!.isShowing, isTrue); + expect(find.text('panel'), findsOneWidget); + }); + + testWidgets('关闭动画未结束时重新 open 不会被旧 route 回调误清理', (tester) async { + late BuildContext hostContext; + TPopupHandle? handle; + var openCount = 0; + var closedCount = 0; + + await openPopup( + tester, + onPressed: () { + hostContext = tester.element(find.text('open')); + handle = TPopup.show( + hostContext, + options: TPopupOptions.bottom( + height: 100, + cancelBuilder: null, + confirmBuilder: null, + animationDuration: const Duration(milliseconds: 300), + onOpen: () => openCount++, + onClosed: () => closedCount++, + child: const SizedBox(height: 60, child: Text('race panel')), + ), + ); + }, + ); + await tester.pumpAndSettle(); + expect(handle!.isShowing, isTrue); + expect(openCount, 1); + expect(closedCount, 0); + + handle!.close(); + await tester.pump(const Duration(milliseconds: 100)); + + handle!.open(hostContext); + await tester.pump(); + expect(handle!.isShowing, isTrue); + expect(openCount, 2); + + await tester.pumpAndSettle(); + expect(handle!.isShowing, isTrue); + expect(find.text('race panel'), findsOneWidget); + expect(closedCount, 0); + + handle!.close(); + await tester.pumpAndSettle(); + expect(closedCount, 1); + }); + + testWidgets('navigatorContext 失效后 handle.open 仍优先复用缓存 navigator', + (tester) async { + TPopupHandle? handle; + var showLauncher = true; + + await tester.pumpWidget( + MaterialApp( + home: TTheme( + data: TThemeData.defaultData(), + child: StatefulBuilder( + builder: (context, setState) { + return Scaffold( + body: Column( + children: [ + if (showLauncher) + Builder( + builder: (launcherContext) { + return ElevatedButton( + onPressed: () { + handle = TPopup.show( + launcherContext, + navigatorContext: launcherContext, + options: TPopupOptions.bottom( + height: 100, + cancelBuilder: null, + confirmBuilder: null, + child: const SizedBox( + height: 60, + child: Text('cached navigator popup'), + ), + ), + ); + }, + child: const Text('open popup'), + ); + }, + ), + ElevatedButton( + onPressed: () { + setState(() => showLauncher = false); + }, + child: const Text('dispose launcher'), + ), + ], + ), + ); + }, + ), + ), + ), + ); + await tester.tap(find.text('open popup')); + await tester.pumpAndSettle(); + expect(find.text('cached navigator popup'), findsOneWidget); + + handle!.close(); + await tester.pumpAndSettle(); + expect(find.text('cached navigator popup'), findsNothing); + + await tester.tap(find.text('dispose launcher')); + await tester.pumpAndSettle(); + + expect(() => handle!.open(), returnsNormally); + await tester.pumpAndSettle(); + expect(handle!.isShowing, isTrue); + expect(find.text('cached navigator popup'), findsOneWidget); + }); + + testWidgets('非栈顶 handle.close 会立即移除 route 并触发 onClosed', (tester) async { + TPopupHandle? outerHandle; + TPopupHandle? innerHandle; + var outerClosedCount = 0; + + await openPopup( + tester, + onPressed: () { + outerHandle = TPopup.show( + tester.element(find.text('open')), + options: TPopupOptions.bottom( + height: 160, + animationDuration: const Duration(milliseconds: 300), + cancelBuilder: null, + confirmBuilder: null, + onClosed: () => outerClosedCount++, + child: Builder( + builder: (ctx) { + return Column( + children: [ + const Text('outer immediate remove'), + ElevatedButton( + onPressed: () { + innerHandle = TPopup.show( + ctx, + options: TPopupOptions.center( + width: 120, + height: 80, + closeBuilder: null, + child: const SizedBox( + width: 120, + height: 80, + child: Text('inner immediate remove'), + ), + ), + ); + }, + child: const Text('open inner immediate'), + ), + ], + ); + }, + ), + ), + ); + }, + ); + await tester.pumpAndSettle(); + await tester.tap(find.text('open inner immediate')); + await tester.pumpAndSettle(); + + outerHandle!.close(); + await tester.pump(); + expect(find.text('outer immediate remove'), findsNothing); + expect(find.text('inner immediate remove'), findsOneWidget); + expect(outerClosedCount, 1); + + innerHandle!.close(); + await tester.pumpAndSettle(); + }); + + testWidgets('handle.open 在已展示时无副作用', (tester) async { + late BuildContext hostContext; + TPopupHandle? handle; + + await openPopup( + tester, + onPressed: () { + hostContext = tester.element(find.text('open')); + handle = TPopup.show( + hostContext, + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 80, + cancelBuilder: null, + confirmBuilder: null, + child: const SizedBox(height: 40)), + ); + }, + ); + await tester.pumpAndSettle(); + expect(handle!.isShowing, isTrue); + + handle!.open(hostContext); + await tester.pumpAndSettle(); + expect(handle!.isShowing, isTrue); + }); + + testWidgets('非法参数组合矩阵在 show 时直接抛 FlutterError', (tester) async { + late BuildContext hostContext; + + await tester.pumpWidget( + wrapPopupTest( + Builder( + builder: (context) { + hostContext = context; + return const SizedBox.shrink(); + }, + ), + ), + ); + + void expectInvalidShow(String reason, TPopupOptions options) { + expect( + () => TPopup.show(hostContext, options: options), + throwsA(isA()), + reason: reason, + ); + } + + expectInvalidShow( + '有蒙层但非模态(默认关闭策略)', + TPopupOptions.bottom( + child: const SizedBox(height: 40), + showOverlay: true, + modal: false, + ), + ); + expectInvalidShow( + '有蒙层但非模态(显式 false)', + TPopupOptions.bottom( + child: const SizedBox(height: 40), + showOverlay: true, + modal: false, + closeOnOverlayClick: false, + ), + ); + expectInvalidShow( + '透明模态显式要求蒙层关闭', + TPopupOptions.bottom( + child: const SizedBox(height: 40), + showOverlay: false, + modal: true, + closeOnOverlayClick: true, + ), + ); + expectInvalidShow( + '非模态浮层显式要求蒙层关闭', + TPopupOptions.bottom( + child: const SizedBox(height: 40), + showOverlay: false, + modal: false, + closeOnOverlayClick: true, + ), + ); + }); + }); + + group('TPopup 扩展场景', () { + testWidgets('top 不渲染头部、仅显示 child(用 .top factory)', (tester) async { + await openPopup( + tester, + onPressed: () { + TPopup.show( + tester.element(find.text('open')), + // .top factory 根本不暴露 titleWidget:编译期就杜绝错位 + options: TPopupOptions.top( + height: 120, + child: const Text('内容'), + ), + ); + }, + ); + await tester.pumpAndSettle(); + // 不应出现默认头部文案(zh 资源) + expect(find.text('取消'), findsNothing); + expect(find.text('确定'), findsNothing); + expect(find.text('内容'), findsOneWidget); + }); + + testWidgets('left / right 侧栏展开内容', (tester) async { + for (final placement in [ + TPopupPlacement.left, + TPopupPlacement.right, + ]) { + late BuildContext hostContext; + TPopupHandle? handle; + await openPopup( + tester, + onPressed: () { + hostContext = tester.element(find.text('open')); + handle = TPopup.show( + hostContext, + options: TPopupOptions( + placement: placement, + width: 240, + child: const SizedBox(height: 40)), + ); + }, + ); + await tester.pumpAndSettle(); + expect(find.byType(Expanded), findsOneWidget); + handle!.close(); + await tester.pumpAndSettle(); + } + }); + + testWidgets('titleWidget + 自定义 cancel/confirm Builder 文案', (tester) async { + await openPopup( + tester, + onPressed: () { + TPopup.show( + tester.element(find.text('open')), + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 180, + titleWidget: const Text('Widget标题'), + cancelBuilder: (_, close) => + GestureDetector(onTap: close, child: const Text('左')), + confirmBuilder: (_, close) => + GestureDetector(onTap: close, child: const Text('右')), + child: const SizedBox(height: 40)), + ); + }, + ); + await tester.pumpAndSettle(); + expect(find.text('Widget标题'), findsOneWidget); + expect(find.text('左'), findsOneWidget); + expect(find.text('右'), findsOneWidget); + }); + + testWidgets('destroyOnClose 与自定义 close 组件', (tester) async { + late BuildContext hostContext; + await openPopup( + tester, + onPressed: () { + hostContext = tester.element(find.text('open')); + TPopup.show( + hostContext, + options: TPopupOptions( + placement: TPopupPlacement.center, + width: 140, + destroyOnClose: true, + closeBuilder: (_, close) => GestureDetector( + onTap: close, + child: const Text('关'), + ), + child: const SizedBox(height: 60, width: 120)), + ); + }, + ); + await tester.pumpAndSettle(); + await tester.tap(find.text('关')); + await tester.pumpAndSettle(); + }); + + testWidgets('onOverlayClick 且点击蒙层关闭', (tester) async { + var overlayClick = 0; + late BuildContext hostContext; + await openPopup( + tester, + onPressed: () { + hostContext = tester.element(find.text('open')); + TPopup.show( + hostContext, + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 100, + onOverlayClick: () => overlayClick++, + child: const SizedBox(height: 60)), + ); + }, + ); + await tester.pumpAndSettle(); + await tester.tapAt(const Offset(10, 10)); + await tester.pumpAndSettle(); + expect(overlayClick, 1); + }); + + testWidgets('show 返回的 handle 关闭后 isShowing 为 false', (tester) async { + TPopupHandle? handle; + await openPopup( + tester, + onPressed: () { + handle = TPopup.show( + tester.element(find.text('open')), + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 80, + child: const SizedBox(height: 40)), + ); + }, + ); + await tester.pumpAndSettle(); + handle!.close(); + await tester.pumpAndSettle(); + expect(handle!.isShowing, isFalse); + handle!.close(); + }); + }); + + group('TPopup 触发源与配置', () { + testWidgets('handle.close 触发 api', (tester) async { + TPopupTrigger? hideTrigger; + TPopupHandle? handle; + + await openPopup( + tester, + onPressed: () { + handle = TPopup.show( + tester.element(find.text('open')), + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 100, + onVisibleChange: (v, t) { + if (!v) { + hideTrigger = t; + } + }, + child: const SizedBox(height: 60)), + ); + }, + ); + await tester.pumpAndSettle(); + handle!.close(); + await tester.pumpAndSettle(); + expect(hideTrigger, TPopupTrigger.api); + }); + + testWidgets('confirm 点击触发 confirm', (tester) async { + TPopupTrigger? hideTrigger; + late BuildContext hostContext; + + await openPopup( + tester, + onPressed: () { + hostContext = tester.element(find.text('open')); + TPopup.show( + hostContext, + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 120, + onVisibleChange: (v, t) { + if (!v) { + hideTrigger = t; + } + }, + child: const SizedBox(height: 60)), + ); + }, + ); + await tester.pumpAndSettle(); + await tester.tap(find.text('确定')); + await tester.pumpAndSettle(); + expect(hideTrigger, TPopupTrigger.confirm); + }); + + testWidgets('destroyOnClose 路由关闭后可再次 show', (tester) async { + late BuildContext hostContext; + TPopupHandle? first; + + await openPopup( + tester, + onPressed: () { + hostContext = tester.element(find.text('open')); + first = TPopup.show( + hostContext, + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 80, + destroyOnClose: true, + cancelBuilder: null, + confirmBuilder: null, + child: const SizedBox(height: 40)), + ); + }, + ); + await tester.pumpAndSettle(); + first!.close(); + await tester.pumpAndSettle(); + expect(first!.isShowing, isFalse); + + TPopupHandle? second; + await openPopup( + tester, + onPressed: () { + second = TPopup.show( + hostContext, + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 80, + destroyOnClose: true, + cancelBuilder: null, + confirmBuilder: null, + child: const SizedBox(height: 40)), + ); + }, + ); + await tester.pumpAndSettle(); + expect(second!.isShowing, isTrue); + second!.close(); + await tester.pumpAndSettle(); + }); + }); + + group('TPopup 自定义控件', () { + testWidgets('自定义 cancel / confirm / close 组件', (tester) async { + await openPopup( + tester, + onPressed: () { + TPopup.show( + tester.element(find.text('open')), + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 200, + cancelBuilder: (_, __) => const Text('自定义取消'), + confirmBuilder: (_, __) => const Text('自定义确认'), + child: const SizedBox(height: 60)), + ); + }, + ); + await tester.pumpAndSettle(); + expect(find.text('自定义取消'), findsOneWidget); + expect(find.text('自定义确认'), findsOneWidget); + }); + + testWidgets('自定义 cancelBuilder 保留业务侧语义', (tester) async { + final semanticsHandle = tester.ensureSemantics(); + try { + await openPopup( + tester, + onPressed: () { + TPopup.show( + tester.element(find.text('open')), + options: TPopupOptions( + placement: TPopupPlacement.bottom, + height: 200, + confirmBuilder: null, + cancelBuilder: (_, close) => Semantics( + container: true, + label: '自定义取消语义', + button: true, + child: GestureDetector( + onTap: close, + child: const Text('自定义取消'), + ), + ), + child: const SizedBox(height: 60), + ), + ); + }, + ); + await tester.pumpAndSettle(); + final semanticsNode = tester.getSemantics(find.text('自定义取消')); + expect(semanticsNode.label, contains('自定义取消语义')); + expect(semanticsNode.label, isNot('取消')); + } finally { + semanticsHandle.dispose(); + } + }); + }); +} diff --git a/tdesign-site/src/action-sheet/README.md b/tdesign-site/src/action-sheet/README.md index 79a1a954c..2c8614a1b 100644 --- a/tdesign-site/src/action-sheet/README.md +++ b/tdesign-site/src/action-sheet/README.md @@ -897,14 +897,12 @@ Widget _buildIconListLeftActionSheet(BuildContext context) { | badge | TBadge? | - | 角标 | | description | String? | - | 描述信息 | | disabled | bool | false | 是否禁用 | -| group | String? | - | 分组,用于带描述多行滚动宫格 | +| group | String? | - | 分组,用于带描述多行滚动宫格 当[TActionSheet.theme]等于[TActionSheetTheme.group]时有效 有效时,如果该值未配置整个[TActionSheetItem]会被忽略,即不会展示 | | icon | Widget? | - | 图标 | | iconSize | double? | - | 图标大小 | | label | String | - | 标题 | | textStyle | TextStyle? | - | 标题样式 | -``` -``` ### TActionSheet #### 简介 @@ -913,23 +911,23 @@ Widget _buildIconListLeftActionSheet(BuildContext context) { | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | +| context | BuildContext | - | 上下文 | | align | TActionSheetAlign | TActionSheetAlign.center | 对齐方式 | | cancelText | String? | - | 取消按钮的文本 | | closeOnOverlayClick | bool | true | 点击蒙层时是否关闭 | -| context | BuildContext | context | 上下文 | -| count | int | 8 | 每页显示的项目数 | -| description | String? | - | 描述文本 | -| itemHeight | double | 96.0 | 项目的行高 | -| itemMinWidth | double | 80.0 | 项目的最小宽度 | +| count | int | 8 | 每页显示的项目数 当[theme]等于[TActionSheetTheme.grid]且[showPagination]为true时有效 | +| description | String? | - | 描述文本 当[theme]等于[TActionSheetTheme.grid]或[theme]等于[TActionSheetTheme.list]时有效 | +| itemHeight | double | 96.0 | 项目的行高 当[theme]等于[TActionSheetTheme.grid]或[theme]等于[TActionSheetTheme.group]时有效 | +| itemMinWidth | double | 80.0 | 项目的最小宽度 当[theme]等于[TActionSheetTheme.grid]且[scrollable]为true时有效 或当[theme]等于[TActionSheetTheme.group]时有效 | | items | List | - | ActionSheet的项目列表 | | onCancel | VoidCallback? | - | 取消按钮的回调函数 | | onClose | VoidCallback? | - | 关闭时的回调函数 | | onSelected | TActionSheetItemCallback? | - | 选择项目时的回调函数 | -| rows | int | 2 | 显示的行数 | -| scrollable | bool | false | 是否可以横向滚动 | +| rows | int | 2 | 显示的行数 当[theme]等于[TActionSheetTheme.grid]时有效 | +| scrollable | bool | false | 是否可以横向滚动 当[theme]等于[TActionSheetTheme.grid]且[showPagination]为false时有效 | | showCancel | bool | true | 是否显示取消按钮 | | showOverlay | bool | true | 是否显示遮罩层 | -| showPagination | bool | false | 是否显示分页 | +| showPagination | bool | false | 是否显示分页 当[theme]等于[TActionSheetTheme.grid]时有效 | | theme | TActionSheetTheme | TActionSheetTheme.list | 主题样式 | | useSafeArea | bool | true | 使用安全区域 | | visible | bool | false | 是否立即显示 | @@ -937,11 +935,106 @@ Widget _buildIconListLeftActionSheet(BuildContext context) { #### 静态方法 -| 名称 | 返回类型 | 参数 | 说明 | +##### TActionSheet.showGridActionSheet + +显示宫格类型面板 + +返回类型:`void` + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| context | BuildContext | - | 上下文 | +| items | List | - | ActionSheet的项目列表 | +| align | TActionSheetAlign | TActionSheetAlign.center | 对齐方式 | +| cancelText | String? | - | 取消按钮的文本 | +| showCancel | bool | true | 是否显示取消按钮 | +| onSelected | TActionSheetItemCallback? | - | 选择项目时的回调函数 | +| showOverlay | bool | true | 是否显示遮罩层 | +| closeOnOverlayClick | bool | true | 点击蒙层时是否关闭 | +| count | int | 8 | 每页显示的项目数 当[theme]等于[TActionSheetTheme.grid]且[showPagination]为true时有效 | +| rows | int | 2 | 显示的行数 当[theme]等于[TActionSheetTheme.grid]时有效 | +| itemHeight | double | 96.0 | 项目的行高 当[theme]等于[TActionSheetTheme.grid]或[theme]等于[TActionSheetTheme.group]时有效 | +| itemMinWidth | double | 80.0 | 项目的最小宽度 当[theme]等于[TActionSheetTheme.grid]且[scrollable]为true时有效 或当[theme]等于[TActionSheetTheme.group]时有效 | +| scrollable | bool | false | 是否可以横向滚动 当[theme]等于[TActionSheetTheme.grid]且[showPagination]为false时有效 | +| showPagination | bool | false | 是否显示分页 当[theme]等于[TActionSheetTheme.grid]时有效 | +| onCancel | VoidCallback? | - | 取消按钮的回调函数 | +| description | String? | - | 描述文本 当[theme]等于[TActionSheetTheme.grid]或[theme]等于[TActionSheetTheme.list]时有效 | +| onClose | VoidCallback? | - | 关闭时的回调函数 | +| useSafeArea | bool | true | 使用安全区域 | + + +##### TActionSheet.showGroupActionSheet + +显示分组类型面板 + +返回类型:`void` + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| context | BuildContext | - | 上下文 | +| items | List | - | ActionSheet的项目列表 | +| align | TActionSheetAlign | TActionSheetAlign.left | 对齐方式 | +| cancelText | String? | - | 取消按钮的文本 | +| showCancel | bool | true | 是否显示取消按钮 | +| onSelected | TActionSheetItemCallback? | - | 选择项目时的回调函数 | +| showOverlay | bool | true | 是否显示遮罩层 | +| closeOnOverlayClick | bool | true | 点击蒙层时是否关闭 | +| itemHeight | double | 96.0 | 项目的行高 当[theme]等于[TActionSheetTheme.grid]或[theme]等于[TActionSheetTheme.group]时有效 | +| itemMinWidth | double | 80.0 | 项目的最小宽度 当[theme]等于[TActionSheetTheme.grid]且[scrollable]为true时有效 或当[theme]等于[TActionSheetTheme.group]时有效 | +| onCancel | VoidCallback? | - | 取消按钮的回调函数 | +| onClose | VoidCallback? | - | 关闭时的回调函数 | +| useSafeArea | bool | true | 使用安全区域 | + + +##### TActionSheet.showListActionSheet + +显示列表类型面板 + +返回类型:`void` + +| 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | -| showGridActionSheet | | required BuildContext context, required List items, TActionSheetAlign align, String? cancelText, bool showCancel, TActionSheetItemCallback? onSelected, bool showOverlay, bool closeOnOverlayClick, int count, int rows, double itemHeight, double itemMinWidth, bool scrollable, bool showPagination, VoidCallback? onCancel, String? description, VoidCallback? onClose, bool useSafeArea, | 显示宫格类型面板 | -| showGroupActionSheet | | required BuildContext context, required List items, TActionSheetAlign align, String? cancelText, bool showCancel, TActionSheetItemCallback? onSelected, bool showOverlay, bool closeOnOverlayClick, double itemHeight, double itemMinWidth, VoidCallback? onCancel, VoidCallback? onClose, bool useSafeArea, | 显示分组类型面板 | -| showListActionSheet | | required BuildContext context, required List items, TActionSheetAlign align, String? cancelText, bool showCancel, VoidCallback? onCancel, TActionSheetItemCallback? onSelected, bool showOverlay, bool closeOnOverlayClick, VoidCallback? onClose, bool useSafeArea, | 显示列表类型面板 | +| context | BuildContext | - | 上下文 | +| items | List | - | ActionSheet的项目列表 | +| align | TActionSheetAlign | TActionSheetAlign.center | 对齐方式 | +| cancelText | String? | - | 取消按钮的文本 | +| showCancel | bool | true | 是否显示取消按钮 | +| onCancel | VoidCallback? | - | 取消按钮的回调函数 | +| onSelected | TActionSheetItemCallback? | - | 选择项目时的回调函数 | +| showOverlay | bool | true | 是否显示遮罩层 | +| closeOnOverlayClick | bool | true | 点击蒙层时是否关闭 | +| onClose | VoidCallback? | - | 关闭时的回调函数 | +| useSafeArea | bool | true | 使用安全区域 | + + +### TActionSheetTheme +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| list | - | +| grid | - | +| group | - | + + +### TActionSheetAlign +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| center | - | +| left | - | +| right | - | + + +### TActionSheetItemCallback +#### 类型定义 + +```dart +typedef TActionSheetItemCallback = void Function(TActionSheetItem item, int index); +``` \ No newline at end of file diff --git a/tdesign-site/src/avatar/README.md b/tdesign-site/src/avatar/README.md index b5f0e4be4..87922dfbb 100644 --- a/tdesign-site/src/avatar/README.md +++ b/tdesign-site/src/avatar/README.md @@ -327,8 +327,8 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; | displayText | String? | - | 纯展示类型末尾文字 | | fit | BoxFit? | - | 自定义图片对齐方式 | | icon | IconData? | - | 自定义图标 | -| key | | - | | -| onTap | Function()? | - | 操作点击事件 | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | +| onTap | Function()? | - | 操作点击事件 | | radius | double? | - | 自定义圆角 | | shape | TAvatarShape | TAvatarShape.circle | 头像形状 | | size | TAvatarSize | TAvatarSize.medium | 头像尺寸 | @@ -336,4 +336,38 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; | type | TAvatarType | TAvatarType.normal | 头像类型 | +### TAvatarSize +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| large | - | +| medium | - | +| small | - | + + +### TAvatarType +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| icon | - | +| normal | - | +| customText | - | +| display | - | +| operation | - | + + +### TAvatarShape +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| circle | - | +| square | - | + + \ No newline at end of file diff --git a/tdesign-site/src/back-top/README.md b/tdesign-site/src/back-top/README.md index a2be1fa25..15324ce21 100644 --- a/tdesign-site/src/back-top/README.md +++ b/tdesign-site/src/back-top/README.md @@ -80,11 +80,31 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | controller | ScrollController? | - | 页面滚动的控制器 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | onClick | VoidCallback? | - | 按钮点击事件 | | showText | bool | false | 是否展示文字 | | style | TBackTopStyle | TBackTopStyle.circle | 样式,圆形和半圆 | | theme | TBackTopTheme | TBackTopTheme.light | 主题 | +### TBackTopTheme +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| light | - | +| dark | - | + + +### TBackTopStyle +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| circle | - | +| halfCircle | - | + + \ No newline at end of file diff --git a/tdesign-site/src/badge/README.md b/tdesign-site/src/badge/README.md index 07dbc85a6..2f629456f 100644 --- a/tdesign-site/src/badge/README.md +++ b/tdesign-site/src/badge/README.md @@ -460,19 +460,52 @@ Medium | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | +| type | TBadgeType | - | 红点样式 | | border | TBadgeBorder | TBadgeBorder.large | 红点圆角大小 | | color | Color? | - | 红点颜色 | | count | String? | - | 红点数量 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | maxCount | String? | '99' | 最大红点数量 | | message | String? | - | 消息内容 | | padding | EdgeInsetsGeometry? | - | 角标自定义padding | | showZero | bool | true | 值为0是否显示 | | size | TBadgeSize | TBadgeSize.small | 红点尺寸 | | textColor | Color? | - | 文字颜色 | -| type | TBadgeType | type | 红点样式 | | widthLarge | double | 32 | 角标大三角形宽 | | widthSmall | double | 12 | 角标小三角形宽 | +### TBadgeType +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| redPoint | 红点样式 | +| message | 消息样式 | +| bubble | 气泡样式 | +| square | 方形样式 | +| subscript | 角标样式 | + + +### TBadgeBorder +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| large | 大圆角 8px | +| small | 小圆角 2px | + + +### TBadgeSize +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| large | 宽 20px | +| small | 宽 16px | + + \ No newline at end of file diff --git a/tdesign-site/src/button/README.md b/tdesign-site/src/button/README.md index b21519c3a..99fb16e95 100644 --- a/tdesign-site/src/button/README.md +++ b/tdesign-site/src/button/README.md @@ -755,7 +755,7 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; | iconTextSpacing | double? | - | 自定义图标与文本之间距离 | | iconWidget | Widget? | - | 自定义图标 icon 控件 | | isBlock | bool | false | 是否为通栏按钮 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | margin | EdgeInsetsGeometry? | - | 自定义 margin | | onLongPress | TButtonEvent? | - | 长按事件 | | onTap | TButtonEvent? | - | 点击事件 | @@ -769,8 +769,6 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; | type | TButtonType | TButtonType.fill | 类型:填充,描边,文字 | | width | double? | - | 自定义宽度 | -``` -``` ### TButtonStyle #### 默认构造方法 @@ -787,12 +785,126 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; #### 工厂构造方法 -| 名称 | 说明 | -| --- | --- | -| TButtonStyle.generateFillStyleByTheme | 生成不同主题的填充按钮样式 | -| TButtonStyle.generateGhostStyleByTheme | 生成不同主题的幽灵按钮样式 | -| TButtonStyle.generateOutlineStyleByTheme | 生成不同主题的描边按钮样式 | -| TButtonStyle.generateTextStyleByTheme | 生成不同主题的文本按钮样式 | +##### TButtonStyle.generateFillStyleByTheme + +生成不同主题的填充按钮样式 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| context | BuildContext | - | - | +| theme | TButtonTheme? | - | - | +| status | TButtonStatus | - | - | + + +##### TButtonStyle.generateGhostStyleByTheme + +生成不同主题的幽灵按钮样式 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| context | BuildContext | - | - | +| theme | TButtonTheme? | - | - | +| status | TButtonStatus | - | - | + + +##### TButtonStyle.generateOutlineStyleByTheme + +生成不同主题的描边按钮样式 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| context | BuildContext | - | - | +| theme | TButtonTheme? | - | - | +| status | TButtonStatus | - | - | + + +##### TButtonStyle.generateTextStyleByTheme + +生成不同主题的文本按钮样式 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| context | BuildContext | - | - | +| theme | TButtonTheme? | - | - | +| status | TButtonStatus | - | - | + + +### TButtonSize +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| large | - | +| medium | - | +| small | - | +| extraSmall | - | + + +### TButtonType +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| fill | - | +| outline | - | +| text | - | +| ghost | - | + + +### TButtonShape +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| rectangle | - | +| round | - | +| square | - | +| circle | - | +| filled | - | + + +### TButtonTheme +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| defaultTheme | - | +| primary | - | +| danger | - | +| light | - | + + +### TButtonStatus +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| defaultState | - | +| active | - | +| disable | - | + + +### TButtonIconPosition +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| left | - | +| right | - | + + +### TButtonEvent +#### 类型定义 + +```dart +typedef TButtonEvent = void Function(); +``` \ No newline at end of file diff --git a/tdesign-site/src/calendar/README.md b/tdesign-site/src/calendar/README.md index 5d05d7424..bb1062ed8 100644 --- a/tdesign-site/src/calendar/README.md +++ b/tdesign-site/src/calendar/README.md @@ -1461,7 +1461,7 @@ Widget _buildLunar(BuildContext context) { | format | CalendarFormat? | - | 用于格式化日期的函数,可定义日期前后的显示内容和日期样式 | | height | double? | - | 高度 | | isTimeUnit | bool? | true | 是否显示时间单位 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | maxDate | int? | - | 最大可选的日期(fromMillisecondsSinceEpoch),不传则默认半年后 | | minDate | int? | - | 最小可选的日期(fromMillisecondsSinceEpoch),不传则默认今天 | | monthTitleBuilder | Widget Function(BuildContext context, DateTime monthDate)? | - | 月标题构建器 | @@ -1484,26 +1484,22 @@ Widget _buildLunar(BuildContext context) { | value | List? | - | 当前选择的日期(fromMillisecondsSinceEpoch),不传则默认今天,当 type = single 时数组长度为1 | | width | double? | - | 宽度 | -``` -``` ### TCalendarPopup #### 默认构造方法 | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | +| context | BuildContext | - | 上下文 | | autoClose | bool? | true | 自动关闭;在点击关闭按钮、确认按钮、遮罩层时自动关闭 | | builder | CalendarBuilder? | - | 控件构建器,优先级高于[child] | | child | TCalendar? | - | 日历控件 | | confirmBtn | Widget? | - | 自定义确认按钮 | -| context | BuildContext | context | 上下文 | | onClose | VoidCallback? | - | 关闭时触发 | | onConfirm | void Function(List value)? | - | 点击确认按钮时触发 | | top | double? | - | 距离顶部的距离 | | visible | bool? | - | 默认是否显示日历 | -``` -``` ### TCalendarStyle #### 默认构造方法 @@ -1515,27 +1511,63 @@ Widget _buildLunar(BuildContext context) { | cellStyle | TextStyle? | - | 日期样式 | | cellSuffixStyle | TextStyle? | - | 日期后面的字符串的样式 | | centreColor | Color? | - | 日期范围内背景样式 | -| decoration | | - | | +| decoration | BoxDecoration? | - | - | | monthTitleStyle | TextStyle? | - | body区域 年月文字样式 | | titleCloseColor | Color? | - | header区域 关闭图标的颜色 | | titleMaxLine | int? | - | header区域 [TCalendar.title]的行数 | | titleStyle | TextStyle? | - | header区域 [TCalendar.title]的样式 | | weekdayStyle | TextStyle? | - | header区域 周 文字样式 | +#### 公开属性 + +| 属性 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| bodyPadding | double? | - | 月与月之间的垂直间距 | +| todayStyle | TextStyle? | - | 当天日期样式 | +| verticalGap | double? | - | 日期垂直间距,水平间距为[verticalGap] / 2 | + #### 工厂构造方法 -| 名称 | 说明 | -| --- | --- | -| TCalendarStyle.cellStyle | 日期样式 | -| TCalendarStyle.generateStyle | 生成默认样式 | +##### TCalendarStyle.cellStyle + +日期样式 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| context | BuildContext | - | - | +| type | DateSelectType? | - | - | + + +##### TCalendarStyle.generateStyle + +生成默认样式 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| context | BuildContext | - | - | -``` -``` ### TCalendarDataSource -``` -``` +#### 简介 +日历数据源接口 + + 开发者需要实现此接口来提供农历转换能力。 + 组件内部不包含农历算法和数据,完全依赖外部实现。 + +#### 方法 + +| 名称 | 返回类型 | 参数 | 说明 | +| --- | --- | --- | --- | +| getLunarInfo | TLunarInfo? | required DateTime solarDate | 获取指定阳历日期的农历信息 [solarDate] 阳历日期 返回 null 表示不显示农历信息 | +| formatDate | String | required DateTime date, required TCalendarDateType type, TLunarInfo? lunarInfo | 格式化日期文本 [date] 阳历日期 [type] 日历类型 [lunarInfo] 农历信息(可选) 返回格式化后的日期字符串 | +| getSolarTerm | String? | required DateTime date | 获取节气信息(可选实现) [date] 阳历日期 返回节气名称,如"春分"、"秋分"等,无节气则返回 null | +| getFestival | String? | required DateTime date, TLunarInfo? lunarInfo | 获取节日信息(可选实现) [date] 阳历日期 [lunarInfo] 农历信息(可选) 返回节日名称,如"春节"、"中秋节"等,无节日则返回 null | +| getHolidayInfo | Map? | required DateTime date | 获取假期信息(可选实现) [date] 阳历日期 返回假期类型和名称: - 'holiday': 法定节假日/公共假期(如"国庆节") - 'workday': 调休工作日(如"补班") - null: 正常日期 示例返回值: - {'type': 'holiday', 'name': '国庆节'} - {'type': 'workday', 'name': '补班'} - null | +| formatYear | String | required int year, required TCalendarDateType type | 格式化年份文本 [year] 年份 [type] 日历类型 返回格式化后的年份字符串 阳历示例:2025 -> "2025年" 阴历示例:2025 -> "二〇二五年" | +| formatMonth | String | required int month, required TCalendarDateType type, bool isLeapMonth | 格式化月份文本 [month] 月份(1-12) [type] 日历类型 [isLeapMonth] 是否是闰月(仅农历有效) 返回格式化后的月份字符串 阳历示例:3 -> "3月" 阴历示例:3 -> "三月",闰3月 -> "闰三月" | +| formatDay | String | required int day, required TCalendarDateType type | 格式化日期文本 [day] 日期(1-31) [type] 日历类型 返回格式化后的日期字符串 阳历示例:7 -> "7日" 阴历示例:7 -> "初七" | + ### TLunarInfo #### 默认构造方法 @@ -1551,4 +1583,68 @@ Widget _buildLunar(BuildContext context) { | yearText | String | - | 年份文本(如:二〇二五) | +### TCalendarDateType +#### 简介 +日历类型枚举 +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| solar | 阳历(公历) | +| lunar | 阴历(农历) | + + +### CalendarType +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| single | - | +| multiple | - | +| range | - | + + +### CalendarTrigger +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| closeBtn | - | +| confirmBtn | - | +| overlay | - | + + +### DateSelectType +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| selected | - | +| disabled | - | +| start | - | +| centre | - | +| end | - | +| empty | - | + + +### CalendarFormat +#### 类型定义 + +```dart +typedef CalendarFormat = TDate? Function(TDate? day); +``` + + +### CalendarBuilder +#### 类型定义 + +```dart +typedef CalendarBuilder = Widget Function(BuildContext context); +``` + + \ No newline at end of file diff --git a/tdesign-site/src/cascader/README.md b/tdesign-site/src/cascader/README.md index f7482529e..4a310a832 100644 --- a/tdesign-site/src/cascader/README.md +++ b/tdesign-site/src/cascader/README.md @@ -249,7 +249,7 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; | initialData | String? | - | 初始化数据 | | initialIndexes | List? | - | 若为null表示全部从零开始 | | isLetterSort | bool | false | 是否开启字母排序 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | onChange | MultiCascaderCallback | - | 值发生变更时触发 | | onClose | Function? | - | 选择器关闭按钮回调 | | subTitles | List? | - | 每级展示的次标题 | @@ -259,4 +259,12 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; | topRadius | double? | - | 顶部圆角 | +### MultiCascaderCallback +#### 类型定义 + +```dart +typedef MultiCascaderCallback = void Function(List selected); +``` + + \ No newline at end of file diff --git a/tdesign-site/src/cell/README.md b/tdesign-site/src/cell/README.md index 163f51ad7..d345ad750 100644 --- a/tdesign-site/src/cell/README.md +++ b/tdesign-site/src/cell/README.md @@ -177,7 +177,7 @@ Widget _buildCard(BuildContext context) { | imageCircle | double? | 50 | 主图圆角,默认50(圆形) | | imageSize | double? | - | 主图尺寸 | | imageWidget | Widget? | - | 主图组件 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | leftIcon | IconData? | - | 左侧图标,出现在单元格标题的左侧 | | leftIconWidget | Widget? | - | 左侧图标组件 | | note | String? | - | 和标题同行的说明文字 | @@ -194,8 +194,6 @@ Widget _buildCard(BuildContext context) { | title | String? | - | 标题 | | titleWidget | Widget? | - | 标题组件 | -``` -``` ### TCellGroup #### 简介 @@ -208,15 +206,13 @@ Widget _buildCard(BuildContext context) { | builder | CellBuilder? | - | cell构建器,可自定义cell父组件,如Dismissible | | cells | List | - | 单元格列表 | | isShowLastBordered | bool? | false | 是否显示最后一个cell的下边框 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | scrollable | bool? | false | 可滚动 | | style | TCellStyle? | - | 自定义样式 | | theme | TCellGroupTheme? | TCellGroupTheme.defaultTheme | 单元格组风格。可选项:default/card | | title | String? | - | 单元格组标题 | | titleWidget | Widget? | - | 单元格组标题组件 | -``` -``` ### TCellStyle #### 简介 @@ -247,9 +243,50 @@ Widget _buildCard(BuildContext context) { #### 工厂构造方法 -| 名称 | 说明 | -| --- | --- | -| TCellStyle.cellStyle | 生成单元格默认样式 | +##### TCellStyle.cellStyle + +生成单元格默认样式 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| context | BuildContext | - | 传递context,会生成默认样式 | + + +### TCellAlign +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| top | - | +| middle | - | +| bottom | - | + + +### TCellGroupTheme +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| defaultTheme | - | +| cardTheme | - | + + +### TCellClick +#### 类型定义 + +```dart +typedef TCellClick = void Function(TCell cell); +``` + + +### CellBuilder +#### 类型定义 + +```dart +typedef CellBuilder = Widget Function(BuildContext context, TCell cell, int index); +``` \ No newline at end of file diff --git a/tdesign-site/src/checkbox/README.md b/tdesign-site/src/checkbox/README.md index 3fde5c091..1252d2903 100644 --- a/tdesign-site/src/checkbox/README.md +++ b/tdesign-site/src/checkbox/README.md @@ -434,16 +434,16 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; | backgroundColor | Color? | - | 背景颜色 | | cardMode | bool | false | 展示为卡片模式 | | checkBoxLeftSpace | double? | - | 选项框左侧间距 | -| checked | bool | false | 选中状态。默认为`false` | +| checked | bool | false | 选中状态。默认为`false` 当FuiCheckBox嵌入到FuiCheckBoxGroup的时候,这个值表示初始状态,后续的状态会由Group管理 | | contentDirection | TContentDirection | TContentDirection.right | 文字相对icon的方位 | | customContentBuilder | ContentBuilder? | - | 完全自定义内容 | | customIconBuilder | IconBuilder? | - | 自定义Checkbox显示样式 | | customSpace | EdgeInsetsGeometry? | - | 自定义组件间距 | | disableColor | Color? | - | 禁用选择颜色 | | enable | bool | true | 不可用 | -| id | String? | - | id | +| id | String? | - | id 当FuiCheckBox嵌入到FuiCheckBoxGroup内时,这个值需要赋值,否则不会被纳入Group管理 | | insetSpacing | double? | 16 | 文字和非图标侧的距离 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | onCheckBoxChanged | OnCheckValueChanged? | - | 切换监听 | | selectColor | Color? | - | 选择颜色 | | showDivider | bool | true | 是否展示分割线 | @@ -459,8 +459,6 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; | titleFont | Font? | - | 标题字体大小 | | titleMaxLine | int? | - | 标题的行数 | -``` -``` ### TCheckboxGroup #### 默认构造方法 @@ -468,12 +466,12 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | checkedIds | List? | - | 勾选的CheckBox id列表 | -| child | | - | | +| child | Widget | - | 可以是任意包含TCheckBox的容器,比如: ``` Row( children: [ TCheckBox(), TCheckBox(), ... ] ) ``` | | contentDirection | TContentDirection? | - | 文字相对icon的方位 | | controller | TCheckboxGroupController? | - | 可以通过控制器操作勾选状态 | | customContentBuilder | ContentBuilder? | - | CheckBox完全自定义内容 | | customIconBuilder | IconBuilder? | - | 自定义选择icon的样式 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | maxChecked | int? | - | 最多可以勾选多少 | | onChangeGroup | OnGroupChange? | - | 状态变化监听器 | | onOverloadChecked | VoidCallback? | - | 超过最大可勾选的个数 | @@ -482,4 +480,85 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; | titleMaxLine | int? | - | CheckBox标题的行数 | +### TCheckboxStyle +#### 简介 +选择框的样式 +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| circle | - | +| square | - | +| check | - | + + +### TContentDirection +#### 简介 +内容相对icon的位置,上、下、左、右,默认内容在icon的右边 +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| left | - | +| right | - | + + +### TCheckBoxSize +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| large | - | +| small | - | + + +### IconBuilder +#### 简介 +自定义Icon +#### 类型定义 + +```dart +typedef IconBuilder = Widget? Function(BuildContext context, bool checked); +``` + + +### ContentBuilder +#### 简介 +自定义Content +#### 类型定义 + +```dart +typedef ContentBuilder = Widget Function(BuildContext context, bool checked, String? content); +``` + + +### OnCheckValueChanged +#### 类型定义 + +```dart +typedef OnCheckValueChanged = void Function(bool selected); +``` + + +### OnGroupChange +#### 简介 +CheckBoxGroup变化监听器 +#### 类型定义 + +```dart +typedef OnGroupChange = void Function(List checkedIds); +``` + + +### OnCheckBoxGroupChange +#### 类型定义 + +```dart +typedef OnCheckBoxGroupChange = void Function(List ids); +``` + + \ No newline at end of file diff --git a/tdesign-site/src/collapse/README.md b/tdesign-site/src/collapse/README.md index a1f767ff2..0174ec848 100644 --- a/tdesign-site/src/collapse/README.md +++ b/tdesign-site/src/collapse/README.md @@ -177,16 +177,42 @@ Card Style 卡片样式 | animationDuration | Duration | kThemeAnimationDuration | 折叠面板列表的动画时长 | | children | List | - | 折叠面板列表的子组件 | | elevation | double | 0 | 折叠面板列表的阴影 | -| expansionCallback | ExpansionPanelCallback? | - | 折叠面板列表的回调函数; | -| key | | - | | -| style | TCollapseStyle | TCollapseStyle.block | 折叠面板列表的样式 | +| expansionCallback | ExpansionPanelCallback? | - | 折叠面板列表的回调函数; 回调时,入参为当前点击的折叠面板的索引 index 和是否展开的状态 isExpanded | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | +| style | TCollapseStyle | TCollapseStyle.block | 折叠面板列表的样式 - [TCollapseStyle.block] 通栏风格 - [TCollapseStyle.card] 卡片风格 | + +#### 公开属性 + +| 属性 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| initialOpenPanelValue | Object? | - | 折叠面板列表的默认展开面板的值; 当使用 [TCollapse.accordion] 时,此值生效 | #### 工厂构造方法 -| 名称 | 说明 | -| --- | --- | -| TCollapse.accordion | | +##### TCollapse.accordion + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| children | List | - | 折叠面板列表的子组件 | +| style | TCollapseStyle | TCollapseStyle.block | 折叠面板列表的样式 - [TCollapseStyle.block] 通栏风格 - [TCollapseStyle.card] 卡片风格 | +| expansionCallback | ExpansionPanelCallback? | - | 折叠面板列表的回调函数; 回调时,入参为当前点击的折叠面板的索引 index 和是否展开的状态 isExpanded | +| animationDuration | Duration | kThemeAnimationDuration | 折叠面板列表的动画时长 | +| elevation | double | 0 | 折叠面板列表的阴影 | +| initialOpenPanelValue | Object? | - | 折叠面板列表的默认展开面板的值; 当使用 [TCollapse.accordion] 时,此值生效 | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | + + +### TCollapseStyle +#### 简介 +折叠面板的组件样式 +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| block | Block 通栏风格 | +| card | Card 卡片风格 | \ No newline at end of file diff --git a/tdesign-site/src/dialog/README.md b/tdesign-site/src/dialog/README.md index 287b1a962..68ee496dc 100644 --- a/tdesign-site/src/dialog/README.md +++ b/tdesign-site/src/dialog/README.md @@ -709,19 +709,19 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | backgroundColor | Color? | - | 背景颜色 | -| buttonStyle | | TDialogButtonStyle.normal | | +| buttonStyle | TDialogButtonStyle | TDialogButtonStyle.normal | - | | buttonWidget | Widget? | - | 自定义按钮 | | content | String? | - | 内容 | | contentColor | Color? | - | 内容颜色 | | contentMaxHeight | double | 0 | 内容的最大高度,默认为0,也就是不限制高度 | | contentWidget | Widget? | - | 内容Widget | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | leftBtn | TDialogButtonOptions? | - | 左侧按钮配置 | -| leftBtnAction | Function()? | - | 左侧按钮默认点击 | +| leftBtnAction | Function()? | - | 左侧按钮默认点击 | | padding | EdgeInsets? | const EdgeInsets.fromLTRB(24, 32, 24, 0) | 内容内边距 | | radius | double | 12.0 | 圆角 | | rightBtn | TDialogButtonOptions? | - | 右侧按钮配置 | -| rightBtnAction | Function()? | - | 右侧按钮默认点击 | +| rightBtnAction | Function()? | - | 右侧按钮默认点击 | | showCloseButton | bool? | - | 显示右上角关闭按钮 | | title | String? | - | 标题 | | titleAlignment | AlignmentGeometry? | - | 标题对齐模式 | @@ -730,21 +730,36 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; #### 工厂构造方法 -| 名称 | 说明 | -| --- | --- | -| TAlertDialog.vertical | 纵向按钮排列的对话框 +##### TAlertDialog.vertical - [buttons]参数是必须的,纵向按钮默认样式都是[TButtonTheme.primary] | +纵向按钮排列的对话框 + + [buttons]参数是必须的,纵向按钮默认样式都是[TButtonTheme.primary] + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | +| buttons | List | - | - | +| backgroundColor | Color? | - | 背景颜色 | +| radius | double | 12.0 | 圆角 | +| title | String? | - | 标题 | +| titleColor | Color? | - | 标题颜色 | +| titleAlignment | AlignmentGeometry? | - | 标题对齐模式 | +| contentWidget | Widget? | - | 内容Widget | +| content | String? | - | 内容 | +| contentColor | Color? | - | 内容颜色 | +| contentMaxHeight | double | 0 | 内容的最大高度,默认为0,也就是不限制高度 | +| showCloseButton | bool? | - | 显示右上角关闭按钮 | +| padding | EdgeInsets? | const EdgeInsets.fromLTRB(24, 32, 24, 0) | 内容内边距 | +| buttonWidget | Widget? | - | 自定义按钮 | -``` -``` ### TConfirmDialog #### 默认构造方法 | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | -| action | Function()? | - | 点击 | +| action | Function()? | - | 点击 | | backgroundColor | Color? | - | 背景颜色 | | buttonStyle | TDialogButtonStyle | TDialogButtonStyle.normal | 按钮样式 | | buttonStyleCustom | TButtonStyle? | - | 按钮自定义样式属性,背景色、边框... | @@ -755,35 +770,31 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; | contentColor | Color? | - | 内容颜色 | | contentMaxHeight | double | 0 | 内容的最大高度,默认为0,也就是不限制高度 | | contentWidget | Widget? | - | 内容Widget | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | padding | EdgeInsets? | const EdgeInsets.fromLTRB(24, 32, 24, 0) | 内容内边距 | | radius | double | 12.0 | 圆角 | | showCloseButton | bool? | - | 右上角关闭按钮 | | title | String? | - | 标题 | | titleAlignment | AlignmentGeometry? | - | 标题对齐模式 | | titleColor | Color? | - | 标题颜色 | -| width | | - | | +| width | double? | - | - | -``` -``` ### TDialogButtonOptions #### 默认构造方法 | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | -| action | Function()? | - | 点击操作 | +| action | Function()? | - | 点击操作 | | fontWeight | FontWeight? | - | 字体粗细 | -| height | double? | - | 按钮高度 | -| style | TButtonStyle? | - | 按钮样式 | +| height | double? | - | 按钮高度 建议使用默认高度 | +| style | TButtonStyle? | - | 按钮样式 设置单个按钮的样式会覆盖Dialog的默认样式 | | theme | TButtonTheme? | - | 按钮类型 | | title | String | - | 标题内容 | | titleColor | Color? | - | 标题颜色 | | titleSize | double? | - | 字体大小 | | type | TButtonType? | - | 按钮类型 | -``` -``` ### TDialogScaffold #### 默认构造方法 @@ -792,25 +803,21 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; | --- | --- | --- | --- | | backgroundColor | Color? | - | 背景色 | | body | Widget | - | Dialog主体 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | radius | double | 12.0 | 圆角 | | showCloseButton | bool? | - | 显示右上角关闭按钮 | | width | double? | - | 弹窗宽度 | -``` -``` ### TDialogTitle #### 默认构造方法 | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | title | String? | - | 标题文字 | | titleColor | Color? | - | 标题颜色 | -``` -``` ### TDialogContent #### 默认构造方法 @@ -819,10 +826,8 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; | --- | --- | --- | --- | | content | String? | - | 标题文字 | | contentColor | Color? | - | 标题颜色 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | -``` -``` ### TDialogInfoWidget #### 默认构造方法 @@ -833,38 +838,32 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; | contentColor | Color? | - | 内容颜色 | | contentMaxHeight | double | 0 | 内容的最大高度,默认为0,也就是不限制高度 | | contentWidget | Widget? | - | 内容Widget | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | padding | EdgeInsetsGeometry? | const EdgeInsets.fromLTRB(24, 32, 24, 0) | 内容的内边距 | | title | String? | - | 标题 | | titleAlignment | AlignmentGeometry? | - | 标题对齐模式 | | titleColor | Color? | - | 标题颜色 | -``` -``` ### HorizontalNormalButtons #### 默认构造方法 | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | leftBtn | TDialogButtonOptions | - | 左按钮 | | rightBtn | TDialogButtonOptions | - | 右按钮 | -``` -``` ### HorizontalTextButtons #### 默认构造方法 | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | leftBtn | TDialogButtonOptions | - | 左按钮 | | rightBtn | TDialogButtonOptions | - | 右按钮 | -``` -``` ### TDialogButton #### 默认构造方法 @@ -880,12 +879,10 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; | buttonType | TButtonType? | - | 按钮类型 | | height | double? | 40.0 | 按钮高度 | | isBlock | bool | true | 按钮高度 | -| key | | - | | -| onPressed | Function() | - | 点击 | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | +| onPressed | Function() | - | 点击 | | width | double? | - | 按钮宽度 | -``` -``` ### TImageDialog #### 默认构造方法 @@ -899,7 +896,7 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; | contentWidget | Widget? | - | 内容Widget | | image | Image | - | 图片 | | imagePosition | TDialogImagePosition? | TDialogImagePosition.top | 图片位置 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | leftBtn | TDialogButtonOptions? | - | 左侧按钮配置 | | padding | EdgeInsets? | - | 内容内边距 | | radius | double | 12.0 | 圆角 | @@ -909,8 +906,6 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; | titleAlignment | AlignmentGeometry? | - | 标题对齐模式 | | titleColor | Color? | - | 标题颜色 | -``` -``` ### TInputDialog #### 默认构造方法 @@ -924,7 +919,7 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; | contentWidget | Widget? | - | 内容Widget | | customInputWidget | Widget? | - | 自定义输入框 | | hintText | String? | '' | 输入提示 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | leftBtn | TDialogButtonOptions? | - | 左侧按钮配置 | | padding | EdgeInsets? | const EdgeInsets.fromLTRB(24, 32, 24, 0) | 内容内边距 | | radius | double | 12.0 | 圆角 | @@ -936,4 +931,29 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; | titleColor | Color? | - | 标题颜色 | +### TDialogButtonStyle +#### 简介 +Dialog按钮样式 + + 用于在Dialog层面配置按钮样式 + Dialog内支持配置每个按钮的样式 +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| normal | - | +| text | - | + + +### TDialogImagePosition +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| top | - | +| middle | - | + + \ No newline at end of file diff --git a/tdesign-site/src/divider/README.md b/tdesign-site/src/divider/README.md index 56fabf0ee..37251b43a 100644 --- a/tdesign-site/src/divider/README.md +++ b/tdesign-site/src/divider/README.md @@ -152,7 +152,7 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; | height | double | 0.5 | 高度,横向线条使用 | | hideLine | bool | false | 隐藏线条,使用纯文本分割 | | isDashed | bool | false | 是否为虚线 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | margin | EdgeInsetsGeometry? | - | 外部填充 | | text | String? | - | 文本字符串,使用默认样式 | | textStyle | TextStyle? | - | 自定义文本样式 | @@ -160,4 +160,15 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; | width | double? | - | 宽度,需要竖向线条时使用 | +### TextAlignment +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| left | - | +| center | - | +| right | - | + + \ No newline at end of file diff --git a/tdesign-site/src/drawer/README.md b/tdesign-site/src/drawer/README.md index 1e4ad50dd..1bdf26a3c 100644 --- a/tdesign-site/src/drawer/README.md +++ b/tdesign-site/src/drawer/README.md @@ -292,11 +292,11 @@ Widget _buildBottomSimple(BuildContext context) { | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | +| context | BuildContext | - | 上下文 | | backgroundColor | Color? | - | 组件背景颜色 | | bordered | bool? | true | 是否显示边框 | | closeOnOverlayClick | bool? | true | 点击蒙层时是否关闭抽屉 | | contentWidget | Widget? | - | 自定义内容,优先级高于[items]/[footer]/[title] | -| context | BuildContext | context | 上下文 | | drawerTop | double? | - | 距离顶部的距离 | | footer | Widget? | - | 抽屉的底部 | | hover | bool? | true | 是否开启点击反馈 | @@ -312,8 +312,6 @@ Widget _buildBottomSimple(BuildContext context) { | visible | bool? | - | 组件是否可见 | | width | double? | 280 | 宽度 | -``` -``` ### TDrawerWidget #### 简介 @@ -330,15 +328,13 @@ Widget _buildBottomSimple(BuildContext context) { | hover | bool? | true | 是否开启点击反馈 | | isShowLastBordered | bool? | true | 是否显示最后一行分割线 | | items | List? | - | 抽屉里的列表项 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | onItemClick | TDrawerItemClickCallback? | - | 点击抽屉里的列表项触发 | | style | TCellStyle? | - | 列表自定义样式 | | title | String? | - | 抽屉的标题 | | titleWidget | Widget? | - | 抽屉的标题组件 | | width | double? | 280 | 宽度 | -``` -``` ### TDrawerItem #### 简介 @@ -352,4 +348,24 @@ Widget _buildBottomSimple(BuildContext context) { | title | String? | - | 每列标题 | +### TDrawerPlacement +#### 简介 +抽屉方向 +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| left | - | +| right | - | + + +### TDrawerItemClickCallback +#### 类型定义 + +```dart +typedef TDrawerItemClickCallback = void Function(int index, TDrawerItem item); +``` + + \ No newline at end of file diff --git a/tdesign-site/src/dropdown-menu/README.md b/tdesign-site/src/dropdown-menu/README.md index ec977c8ed..cfa91abfd 100644 --- a/tdesign-site/src/dropdown-menu/README.md +++ b/tdesign-site/src/dropdown-menu/README.md @@ -283,7 +283,7 @@ TDropdownMenu _buildGroup(BuildContext context) { | height | double? | 48 | menu的高度 | | isScrollable | bool? | false | 是否开启滚动列表 | | items | List? | - | 下拉菜单 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | labelBuilder | LabelBuilder? | - | 自定义标签内容 | | onMenuClosed | ValueChanged? | - | 关闭菜单事件 | | onMenuOpened | ValueChanged? | - | 展开菜单事件 | @@ -291,8 +291,6 @@ TDropdownMenu _buildGroup(BuildContext context) { | tabBarAlign | MainAxisAlignment? | MainAxisAlignment.center | [TDropdownItem.label]和[arrowIcon]/[TDropdownItem.arrowIcon]的对齐方式 | | width | double? | - | menu的宽度 | -``` -``` ### TDropdownItem #### 简介 @@ -306,7 +304,7 @@ TDropdownMenu _buildGroup(BuildContext context) { | builder | TDropdownItemContentBuilder? | - | 完全自定义展示内容 | | controller | TDropdownItemController? | - | 下拉菜单控制器 | | disabled | bool? | false | 是否禁用 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | label | String? | - | 标题 | | maxHeight | double? | - | 内容最大高度 | | minHeight | double? | - | 内容最小高度 | @@ -320,8 +318,12 @@ TDropdownMenu _buildGroup(BuildContext context) { | tabBarFlex | int? | 1 | 该item在menu上的宽度占比,仅在[TDropdownMenu.isScrollable]为false时有效 | | tabBarWidth | double? | - | 该item在menu上的宽度,仅在[TDropdownMenu.isScrollable]为true时有效 | -``` -``` +#### 静态成员 + +| 名称 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| operateHeight | double | - | - | + ### TDropdownItemOption #### 简介 @@ -338,11 +340,58 @@ TDropdownMenu _buildGroup(BuildContext context) { | selectedColor | Color? | - | 选中颜色 | | value | String | - | 选项值 | -``` -``` ### TDropdownItemController #### 简介 下拉菜单控制器 +### TDropdownMenuDirection +#### 简介 +菜单展开方向 +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| down | 向下 | +| up | 向上 | +| auto | 根据内容高度动态展示方向 | + + +### TDropdownItemContentBuilder +#### 类型定义 + +```dart +typedef TDropdownItemContentBuilder = Widget Function(BuildContext context, _TDropdownItemState itemState, TDropdownPopup? popupState); +``` + + +### TDropdownItemOptionsCallback +#### 类型定义 + +```dart +typedef TDropdownItemOptionsCallback = void Function(List? options); +``` + + +### TDropdownItemBuilder +#### 简介 +下拉菜单构建器 +#### 类型定义 + +```dart +typedef TDropdownItemBuilder = List Function(BuildContext context); +``` + + +### LabelBuilder +#### 简介 +自定义标签内容 +#### 类型定义 + +```dart +typedef LabelBuilder = Widget Function(BuildContext context, String label, bool isOpened, int index); +``` + + \ No newline at end of file diff --git a/tdesign-site/src/empty/README.md b/tdesign-site/src/empty/README.md index 8f5075b70..b7ed502d3 100644 --- a/tdesign-site/src/empty/README.md +++ b/tdesign-site/src/empty/README.md @@ -131,11 +131,29 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; | emptyTextFont | Font? | - | 描述文字大小 | | icon | IconData? | TIcons.info_circle_filled | 图标 | | image | Widget? | - | 展示图片 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | onTapEvent | TTapEvent? | - | 点击事件 | | operationText | String? | - | 操作按钮文案 | | operationTheme | TButtonTheme? | - | 操作按钮文案主题色 | | type | TEmptyType | TEmptyType.plain | 类型,为operation有操作按钮,plain无按钮 | +### TEmptyType +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| plain | - | +| operation | - | + + +### TTapEvent +#### 类型定义 + +```dart +typedef TTapEvent = void Function(); +``` + + \ No newline at end of file diff --git a/tdesign-site/src/fab/README.md b/tdesign-site/src/fab/README.md index c24020665..4badca22f 100644 --- a/tdesign-site/src/fab/README.md +++ b/tdesign-site/src/fab/README.md @@ -167,7 +167,7 @@ Fab Size 悬浮按钮尺寸 | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | icon | Icon? | - | 图标 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | onClick | VoidCallback? | - | 点击事件 | | shape | TFabShape | TFabShape.circle | 形状 | | size | TFabSize | TFabSize.large | 大小 | @@ -175,4 +175,38 @@ Fab Size 悬浮按钮尺寸 | theme | TFabTheme | TFabTheme.defaultTheme | 主题 | +### TFabTheme +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| primary | - | +| defaultTheme | - | +| light | - | +| danger | - | + + +### TFabShape +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| circle | - | +| square | - | + + +### TFabSize +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| large | - | +| medium | - | +| small | - | +| extraSmall | - | + + \ No newline at end of file diff --git a/tdesign-site/src/footer/README.md b/tdesign-site/src/footer/README.md index d6323dbda..d761bcc58 100644 --- a/tdesign-site/src/footer/README.md +++ b/tdesign-site/src/footer/README.md @@ -117,13 +117,24 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | +| type | TFooterType | - | 样式 | | height | double? | - | 自定义图片高 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | links | List | const [] | 链接 | | logo | String? | - | 品牌图片 | | text | String | '' | 文字 | -| type | TFooterType | type | 样式 | | width | double? | - | 自定义图片宽 | +### TFooterType +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| text | 文字样式 | +| link | 链接样式 | +| brand | 品牌样式 | + + \ No newline at end of file diff --git a/tdesign-site/src/form/README.md b/tdesign-site/src/form/README.md index 25d02941b..02c02781a 100644 --- a/tdesign-site/src/form/README.md +++ b/tdesign-site/src/form/README.md @@ -719,24 +719,22 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; | data | Map | - | 表单数据 | | disabled | bool | false | 是否禁用整个表单 | | errorMessage | Object? | - | 表单信息错误信息配置 | -| formContentAlign | TextAlign | TextAlign.left | 表单内容对齐方式: 左对齐、右对齐、居中对齐 | +| formContentAlign | TextAlign | TextAlign.left | 表单内容对齐方式: 左对齐、右对齐、居中对齐 可选项: left/right/center 默认为左对齐 优先级低于 TFormItem 的对齐 API TODO: TStepper TRate 等组件没用实现通用性 | | formController | FormController? | - | 表单控制器 | -| formLabelAlign | TextAlign? | TextAlign.left | 表单字段标签的对齐方式: | -| formShowErrorMessage | bool? | true | 校验不通过时,是否显示错误提示信息,统一控制全部表单项 | +| formLabelAlign | TextAlign? | TextAlign.left | 表单字段标签的对齐方式: 左对齐、右对齐、顶部对齐 可选项: left/right/top TODO: 表单总体标签对齐方式 | +| formShowErrorMessage | bool? | true | 校验不通过时,是否显示错误提示信息,统一控制全部表单项 如果希望控制单个表单项,请给 FormItem 设置该属性 | | isHorizontal | bool | true | 表单排列方式是否为 水平方向 | | items | List | - | 表单内容 items | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | labelWidth | double? | 20.0 | 可以整体设置 label 标签宽度 | | onReset | Function? | - | 表单重置时触发 | | onSubmit | Function | - | 表单提交时触发 | -| preventSubmitDefault | bool? | true | 是否阻止表单提交默认事件(表单提交默认事件会刷新页面) | +| preventSubmitDefault | bool? | true | 是否阻止表单提交默认事件(表单提交默认事件会刷新页面) 设置为 true 可以避免刷新 | | requiredMark | bool? | true | 是否显示必填符号(*),默认显示 | | rules | Map | - | 整个表单字段校验规则 | -| scrollToFirstError | String? | - | 表单校验不通过时,是否自动滚动到第一个校验不通过的字段,平滑滚动或是瞬间直达。 | +| scrollToFirstError | String? | - | 表单校验不通过时,是否自动滚动到第一个校验不通过的字段,平滑滚动或是瞬间直达。 值为空则表示不滚动。可选项:''/smooth/auto | | submitWithWarningMessage | bool? | false | 【讨论中】当校验结果只有告警信息时,是否触发 submit 提交事件 | -``` -``` ### TFormItem #### 默认构造方法 @@ -746,20 +744,20 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; | additionInfo | String? | - | TInput的辅助信息 | | backgroundColor | Color? | - | 背景色 | | child | Widget? | - | 表单子组件 | -| contentAlign | TextAlign? | - | 表单显示内容对齐方式: | -| formItemNotifier | | - | | +| contentAlign | TextAlign? | - | 表单显示内容对齐方式: left、right、top TODO: TStepper TRate 等组件没用实现通用性 | +| formItemNotifier | FormItemNotifier? | - | - | | formRules | List? | - | 整个表单的校验规则 | | help | String? | - | TInput 默认显示文字 | -| hintText | null | '' | 提示内容 | +| hintText | - | '' | 提示内容 | | indicator | bool? | - | TTextarea 的属性,指示器 | | itemRule | List? | - | 表单项验证规则 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | label | String? | - | 表单项标签左侧展示的内容 | -| labelAlign | TextAlign? | - | TODO: item 标签对齐方式 | +| labelAlign | TextAlign? | - | TODO: item 标签对齐方式 可选: left、right、top | | labelWidget | Widget? | - | 自定义标签 | | labelWidth | double? | - | 标签宽度,如果提供则覆盖Form的labelWidth | | name | String? | - | 表单字段名称 | -| radios | | - | | +| radios | Map? | - | - | | requiredMark | bool? | true | 是否显示必填标记(*) | | select | String | '' | 选择器 适用于日期选择器等 | | selectFn | Function? | - | 选择器方法 适用于日期选择器等 | @@ -767,8 +765,6 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; | tipAlign | TextAlign? | - | 组件提示内容对齐方式 | | type | TFormItemType | - | 表格单元需要使用的组件类型 | -``` -``` ### TFormValidation #### 默认构造方法 @@ -780,4 +776,22 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; | validate | String? Function(dynamic) | - | 校验方法 | +### TFormItemType +#### 简介 +表格单元选用组件类型的枚举 +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| input | - | +| radios | - | +| dateTimePicker | - | +| cascader | - | +| stepper | - | +| rate | - | +| textarea | - | +| upLoadImg | - | + + \ No newline at end of file diff --git a/tdesign-site/src/image-viewer/README.md b/tdesign-site/src/image-viewer/README.md index 4f159dc3e..f48679c44 100644 --- a/tdesign-site/src/image-viewer/README.md +++ b/tdesign-site/src/image-viewer/README.md @@ -73,12 +73,42 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; #### 静态方法 -| 名称 | 返回类型 | 参数 | 说明 | +##### TImageViewer.showImageViewer + +显示图片预览 + +返回类型:`void` + +| 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | -| showImageViewer | | required BuildContext context, required List images, List? labels, bool? closeBtn, bool? deleteBtn, bool? showIndex, bool? loop, bool? autoplay, int? duration, Color? bgColor, Color? navBarBgColor, Color? iconColor, TextStyle? labelStyle, TextStyle? indexStyle, Color? modalBarrierColor, bool? barrierDismissible, int? defaultIndex, double? width, double? height, OnIndexChange? onIndexChange, OnClose? onClose, OnDelete? onDelete, bool? ignoreDeleteError, OnImageTap? onTap, OnLongPress? onLongPress, LeftItemBuilder? leftItemBuilder, RightItemBuilder? rightItemBuilder, | 显示图片预览 | +| context | BuildContext | - | - | +| images | List | - | 图片数组 | +| labels | List? | - | 图片描述 | +| closeBtn | bool? | true | 是否展示关闭按钮 | +| deleteBtn | bool? | false | 是否显示删除操作 | +| showIndex | bool? | false | 是否显示页码 | +| loop | bool? | false | 图片是否循环 | +| autoplay | bool? | false | 图片轮播是否自动播放 | +| duration | int? | - | 自动播放间隔 | +| bgColor | Color? | - | 背景色 | +| navBarBgColor | Color? | - | 导航栏背景色 | +| iconColor | Color? | - | 图标颜色 | +| labelStyle | TextStyle? | - | label文字样式 | +| indexStyle | TextStyle? | - | 页码样式 | +| modalBarrierColor | Color? | - | - | +| barrierDismissible | bool? | - | - | +| defaultIndex | int? | - | 默认预览图片所在的下标 | +| width | double? | - | 图片宽度 | +| height | double? | - | 图片高度 | +| onIndexChange | OnIndexChange? | - | 预览图片切换回调 | +| onClose | OnClose? | - | 关闭点击 | +| onDelete | OnDelete? | - | 删除点击 | +| ignoreDeleteError | bool? | - | 是否忽略单张图片删除错误提示 | +| onTap | OnImageTap? | - | 点击图片 | +| onLongPress | OnLongPress? | - | 长按图片 | +| leftItemBuilder | LeftItemBuilder? | - | 左侧自定义操作 | +| rightItemBuilder | RightItemBuilder? | - | 右侧自定义操作 | -``` -``` ### TImageViewerWidget #### 默认构造方法 @@ -96,7 +126,7 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; | ignoreDeleteError | bool? | false | 是否忽略单张图片删除错误提示 | | images | List | - | 图片数组 | | indexStyle | TextStyle? | - | 页码样式 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | labels | List? | - | 图片描述 | | labelStyle | TextStyle? | - | label文字样式 | | leftItemBuilder | LeftItemBuilder? | - | 左侧自定义操作 | @@ -112,4 +142,60 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; | width | double? | - | 图片宽度 | +### OnIndexChange +#### 类型定义 + +```dart +typedef OnIndexChange = Function(int index); +``` + + +### OnClose +#### 类型定义 + +```dart +typedef OnClose = Function(int index); +``` + + +### OnDelete +#### 类型定义 + +```dart +typedef OnDelete = Function(int index); +``` + + +### OnImageTap +#### 类型定义 + +```dart +typedef OnImageTap = Function(int index); +``` + + +### OnLongPress +#### 类型定义 + +```dart +typedef OnLongPress = Function(int index); +``` + + +### LeftItemBuilder +#### 类型定义 + +```dart +typedef LeftItemBuilder = Widget Function(BuildContext context, int index); +``` + + +### RightItemBuilder +#### 类型定义 + +```dart +typedef RightItemBuilder = Widget Function(BuildContext context, int index); +``` + + \ No newline at end of file diff --git a/tdesign-site/src/image/README.md b/tdesign-site/src/image/README.md index 03464495a..ee95db5cb 100644 --- a/tdesign-site/src/image/README.md +++ b/tdesign-site/src/image/README.md @@ -1906,33 +1906,48 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | -| alignment | | Alignment.center | | +| alignment | AlignmentGeometry | Alignment.center | - | | assetUrl | String? | - | 本地素材地址 | -| cacheHeight | | - | | -| cacheWidth | | - | | -| centerSlice | | - | | -| color | | - | | -| colorBlendMode | | - | | -| errorBuilder | | - | | +| cacheHeight | int? | - | - | +| cacheWidth | int? | - | - | +| centerSlice | Rect? | - | - | +| color | Color? | - | - | +| colorBlendMode | BlendMode? | - | - | +| errorBuilder | ImageErrorWidgetBuilder? | - | - | | errorWidget | Widget? | - | 失败自定义提示 | -| excludeFromSemantics | | false | | -| filterQuality | | FilterQuality.low | | +| excludeFromSemantics | bool | false | - | +| filterQuality | FilterQuality | FilterQuality.low | - | | fit | BoxFit? | - | 适配样式 | | frameBuilder | ImageFrameBuilder? | - | 以下系统Image属性,释义请参考系统[Image]中注释 | -| gaplessPlayback | | false | | +| gaplessPlayback | bool | false | - | | height | double? | - | 自定义高 | | imageFile | File? | - | 图片文件路径 | | imgUrl | String? | - | 图片地址 | -| isAntiAlias | | false | | -| key | | - | | -| loadingBuilder | | - | | +| isAntiAlias | bool | false | - | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | +| loadingBuilder | ImageLoadingBuilder? | - | - | | loadingWidget | Widget? | - | 加载自定义提示 | -| matchTextDirection | | false | | -| opacity | | - | | -| repeat | | ImageRepeat.noRepeat | | -| semanticLabel | | - | | +| matchTextDirection | bool | false | - | +| opacity | Animation? | - | - | +| repeat | ImageRepeat | ImageRepeat.noRepeat | - | +| semanticLabel | String? | - | - | | type | TImageType | TImageType.roundedSquare | 图片类型 | | width | double? | - | 自定义宽 | +### TImageType +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| clip | 裁剪 | +| fitHeight | 适应高 | +| fitWidth | 适应宽 | +| stretch | 拉伸 | +| square | 方形, | +| roundedSquare | 圆角方形 | +| circle | 圆形 | + + \ No newline at end of file diff --git a/tdesign-site/src/indexes/README.md b/tdesign-site/src/indexes/README.md index 991028b4b..5deedd0b4 100644 --- a/tdesign-site/src/indexes/README.md +++ b/tdesign-site/src/indexes/README.md @@ -36,28 +36,22 @@ Widget _buildSimple(BuildContext context) { theme: TButtonTheme.primary, type: TButtonType.outline, onTap: () { - Navigator.of(context).push( - TSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.right, - modalTop: renderBox?.size.height, - builder: (context) { - return TIndexes( + TPopup.show( + context, + options: TPopupOptions.right( + width: 280, + inset: TPopupRightInset(top: renderBox?.size.height ?? 0), + child: TIndexes( indexList: indexList, builderContent: (context, index) { final list = _list.firstWhere( (element) => element['index'] == index)['children'] as List; return TCellGroup( - cells: list - .map((e) => TCell( - title: e, - )) - .toList(), + cells: list.map((e) => TCell(title: e)).toList(), ); }, - ); - }, - ), + )), ); }, ); @@ -80,28 +74,22 @@ Widget _buildSimple(BuildContext context) { theme: TButtonTheme.primary, type: TButtonType.outline, onTap: () { - Navigator.of(context).push( - TSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.right, - modalTop: renderBox?.size.height, - builder: (context) { - return TIndexes( + TPopup.show( + context, + options: TPopupOptions.right( + width: 280, + inset: TPopupRightInset(top: renderBox?.size.height ?? 0), + child: TIndexes( indexList: indexList, builderContent: (context, index) { final list = _list.firstWhere( (element) => element['index'] == index)['children'] as List; return TCellGroup( - cells: list - .map((e) => TCell( - title: e, - )) - .toList(), + cells: list.map((e) => TCell(title: e)).toList(), ); }, - ); - }, - ), + )), ); }, ); @@ -127,12 +115,12 @@ Widget _buildOther(BuildContext context) { theme: TButtonTheme.primary, type: TButtonType.outline, onTap: () { - Navigator.of(context).push( - TSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.right, - modalTop: renderBox?.size.height, - builder: (context) { - return TIndexes( + TPopup.show( + context, + options: TPopupOptions.right( + width: 280, + inset: TPopupRightInset(top: renderBox?.size.height ?? 0), + child: TIndexes( indexList: indexList, capsuleTheme: true, builderContent: (context, index) { @@ -140,16 +128,10 @@ Widget _buildOther(BuildContext context) { (element) => element['index'] == index)['children'] as List; return TCellGroup( - cells: list - .map((e) => TCell( - title: e, - )) - .toList(), + cells: list.map((e) => TCell(title: e)).toList(), ); }, - ); - }, - ), + )), ); }, ); @@ -172,12 +154,12 @@ Widget _buildOther(BuildContext context) { theme: TButtonTheme.primary, type: TButtonType.outline, onTap: () { - Navigator.of(context).push( - TSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.right, - modalTop: renderBox?.size.height, - builder: (context) { - return TIndexes( + TPopup.show( + context, + options: TPopupOptions.right( + width: 280, + inset: TPopupRightInset(top: renderBox?.size.height ?? 0), + child: TIndexes( indexList: indexList, capsuleTheme: true, builderContent: (context, index) { @@ -185,16 +167,10 @@ Widget _buildOther(BuildContext context) { (element) => element['index'] == index)['children'] as List; return TCellGroup( - cells: list - .map((e) => TCell( - title: e, - )) - .toList(), + cells: list.map((e) => TCell(title: e)).toList(), ); }, - ); - }, - ), + )), ); }, ); @@ -218,7 +194,7 @@ Widget _buildOther(BuildContext context) { | capsuleTheme | bool? | false | 锚点是否为胶囊式样式 | | indexList | List? | - | 索引字符列表。不传默认 A-Z | | indexListMaxHeight | double? | 0.8 | 索引列表最大高度(父容器高度的百分比,默认 0.8) | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | onChange | void Function(String index)? | - | 索引发生变更时触发事件 | | onSelect | void Function(String index)? | - | 点击侧边栏时触发事件 | | reverse | bool? | false | 反方向滚动置顶 | @@ -226,8 +202,6 @@ Widget _buildOther(BuildContext context) { | sticky | bool? | true | 锚点是否吸顶 | | stickyOffset | double? | 0 | 锚点吸顶时与顶部的距离 | -``` -``` ### TIndexesAnchor #### 简介 @@ -239,12 +213,10 @@ Widget _buildOther(BuildContext context) { | activeIndex | ValueNotifier | - | 选中索引 | | builderAnchor | Widget? Function(BuildContext context, String index, bool isPinnedToTop)? | - | 索引锚点构建 | | capsuleTheme | bool | - | 是否为胶囊式样式 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | sticky | bool | - | 索引是否吸顶 | | text | String | - | 锚点文本 | -``` -``` ### TIndexesList #### 简介 @@ -257,7 +229,7 @@ Widget _buildOther(BuildContext context) { | builderIndex | Widget Function(BuildContext context, String index, bool isActive)? | - | 索引文本自定义构建,包括索引激活左侧提示 | | indexList | List | - | 索引字符列表。不传默认 A-Z | | indexListMaxHeight | double | 0.8 | 索引列表最大高度(父容器高度的百分比,默认0.8) | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | onSelect | void Function(String newIndex, String oldIndex) | - | 点击侧边栏时触发事件 | diff --git a/tdesign-site/src/input/README.md b/tdesign-site/src/input/README.md index 32958cd3d..c66dd4440 100644 --- a/tdesign-site/src/input/README.md +++ b/tdesign-site/src/input/README.md @@ -1042,7 +1042,7 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; | inputDecoration | InputDecoration? | - | 自定义输入框样式,默认圆角 | | inputFormatters | List? | - | 显示输入内容,如限制长度(LengthLimitingTextInputFormatter(6)) | | inputType | TextInputType? | - | 键盘类型,数字、字母 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | labelWidget | Widget? | - | leftLabel右侧组件,支持自定义 | | leftContentSpace | double? | - | 输入框内容左侧间距 | | leftIcon | Widget? | - | 带图标的输入框 | @@ -1067,7 +1067,7 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; | selectionControls | TextSelectionControls? | - | 自定义选择控制器 | | showBottomDivider | bool | true | 是否展示底部分割线 | | size | TInputSize | TInputSize.large | 输入框规格 | -| spacer | TInputSpacer | - | 组件各模块间间距 | +| spacer | TInputSpacer? | - | 组件各模块间间距 | | textAlign | TextAlign? | - | 文字对齐方向 | | textInputBackgroundColor | Color? | - | 文本框背景色 | | textStyle | TextStyle? | - | 文本颜色 | @@ -1075,4 +1075,39 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; | width | double? | - | 输入框宽度(TCardStyle时必须设置该参数) | +### TInputType +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| normal | - | +| twoLine | - | +| longText | - | +| special | - | +| normalMaxTwoLine | - | +| cardStyle | - | + + +### TInputSize +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| small | - | +| large | - | + + +### TCardStyle +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| topText | - | +| topTextWithBlueBorder | - | +| errorStyle | - | + + \ No newline at end of file diff --git a/tdesign-site/src/link/README.md b/tdesign-site/src/link/README.md index c4d51d609..de56f59f4 100644 --- a/tdesign-site/src/link/README.md +++ b/tdesign-site/src/link/README.md @@ -150,7 +150,7 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; | color | Color? | - | link 文本的颜色,如果不设置则根据状态和风格进行计算 | | fontSize | double? | - | link 文本的字体大小,如果不设置则根据状态和风格进行计算 | | iconSize | double? | - | link icon 大小,如果不设置则根据状态和风格进行计算 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | label | String | - | link 展示的文本 | | leftGapWithIcon | double? | - | 前置icon和文本之间的间隔,如果不设置则根据状态和风格进行计算 | | linkClick | LinkClick? | - | link 被点击之后所采取的动作,会将uri当做参数传入到该方法当中 | @@ -164,4 +164,61 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; | uri | Uri? | - | link 跳转的uri | +### TLinkType +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| basic | - | +| withUnderline | - | +| withPrefixIcon | - | +| withSuffixIcon | - | + + +### TLinkStyle +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| primary | - | +| defaultStyle | - | +| danger | - | +| warning | - | +| success | - | + + +### TLinkState +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| normal | - | +| active | - | +| disabled | - | + + +### TLinkSize +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| small | - | +| medium | - | +| large | - | + + +### LinkClick +#### 简介 +限制Function类型,防止传递错误的Function,导致参数对不上 +#### 类型定义 + +```dart +typedef LinkClick = Function(Uri? uri); +``` + + \ No newline at end of file diff --git a/tdesign-site/src/loading/README.md b/tdesign-site/src/loading/README.md index cf203e71c..519d3af70 100644 --- a/tdesign-site/src/loading/README.md +++ b/tdesign-site/src/loading/README.md @@ -260,11 +260,37 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; | duration | int | 2000 | 一次刷新的时间,控制动画速度 | | icon | TLoadingIcon? | TLoadingIcon.circle | 图标,支持圆形、点状、菊花状 | | iconColor | Color? | - | 图标颜色 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | refreshWidget | Widget? | - | 失败刷新组件 | | size | TLoadingSize | - | 尺寸 | | text | String? | - | 文案 | | textColor | Color? | - | 文案颜色 | +### TLoadingSize +#### 简介 +Loading 尺寸 +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| small | 小尺寸 | +| medium | 中尺寸 | +| large | 大尺寸 | + + +### TLoadingIcon +#### 简介 +Loading图标 +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| circle | 圆形 | +| point | 点状 | +| activity | 菊花状 | + + \ No newline at end of file diff --git a/tdesign-site/src/message/README.md b/tdesign-site/src/message/README.md index 176eafef1..5ed4b412f 100644 --- a/tdesign-site/src/message/README.md +++ b/tdesign-site/src/message/README.md @@ -295,7 +295,7 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; | content | String? | - | 通知内容 | | duration | int? | 3000 | 消息内置计时器 | | icon | dynamic | true | 自定义消息前面的图标 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | link | dynamic | - | 链接名称 | | marquee | MessageMarquee? | - | 跑马灯效果 | | offset | List? | - | 相对于 placement 的偏移量 | @@ -308,12 +308,26 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; #### 静态方法 -| 名称 | 返回类型 | 参数 | 说明 | +##### TMessage.showMessage + +返回类型:`void` + +| 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | -| showMessage | | required BuildContext context, String? content, bool? visible, int? duration, dynamic closeBtn, dynamic icon, dynamic link, MessageMarquee? marquee, List? offset, MessageTheme? theme, VoidCallback? onCloseBtnClick, VoidCallback? onDurationEnd, VoidCallback? onLinkClick, | | +| context | BuildContext | - | - | +| content | String? | - | 通知内容 | +| visible | bool? | - | 是否显示 | +| duration | int? | - | 消息内置计时器 | +| closeBtn | dynamic | - | 关闭按钮 | +| icon | dynamic | - | 自定义消息前面的图标 | +| link | dynamic | - | 链接名称 | +| marquee | MessageMarquee? | - | 跑马灯效果 | +| offset | List? | - | 相对于 placement 的偏移量 | +| theme | MessageTheme? | - | 消息组件风格 info/success/warning/error | +| onCloseBtnClick | VoidCallback? | - | 点击关闭按钮触发 | +| onDurationEnd | VoidCallback? | - | 计时结束后触发 | +| onLinkClick | VoidCallback? | - | 点击链接文本时触发 | -``` -``` ### MessageMarquee #### 默认构造方法 @@ -324,8 +338,6 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; | loop | int? | - | 循环次数 | | speed | int? | - | 速度 | -``` -``` ### MessageLink #### 默认构造方法 @@ -337,4 +349,18 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; | uri | Uri? | - | 资源链接 | +### MessageTheme +#### 简介 +定义消息主题枚举 +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| info | 普通通知 | +| success | 成功通知 | +| warning | 警示通知 | +| error | 错误通知 | + + \ No newline at end of file diff --git a/tdesign-site/src/navbar/README.md b/tdesign-site/src/navbar/README.md index 10d795948..a92a54be2 100644 --- a/tdesign-site/src/navbar/README.md +++ b/tdesign-site/src/navbar/README.md @@ -303,7 +303,7 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; | centerTitle | bool | true | 标题是否居中 | | flexibleSpace | Widget? | - | 固定背景 | | height | double | 48 | 高度 | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | leftBarItems | List? | - | 左边操作项 | | onBack | VoidCallback? | - | 返回事件 | | opacity | double | 1.0 | 透明度 | @@ -320,8 +320,6 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; | useBorderStyle | bool | false | 是否使用边框模式 | | useDefaultBack | bool | true | 是否使用默认的返回 | -``` -``` ### TNavBarItem #### 默认构造方法 @@ -334,7 +332,15 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; | iconColor | Color? | - | 图标颜色 | | iconSize | double? | 24.0 | 图标尺寸 | | iconWidget | Widget? | - | 图标组件,优先级高于 icon | -| padding | EdgeInsetsGeometry? | - | | +| padding | EdgeInsetsGeometry? | - | 内部填充 | + + +### TBarItemAction +#### 类型定义 + +```dart +typedef TBarItemAction = void Function(); +``` \ No newline at end of file diff --git a/tdesign-site/src/notice-bar/README.md b/tdesign-site/src/notice-bar/README.md index f9b95f595..6ad02fea7 100644 --- a/tdesign-site/src/notice-bar/README.md +++ b/tdesign-site/src/notice-bar/README.md @@ -286,8 +286,6 @@ Widget _cardNoticeBar(BuildContext context) { ## API ### TNoticeBar -#### 简介 - #### 默认构造方法 | 参数 | 类型 | 默认值 | 说明 | @@ -297,7 +295,7 @@ Widget _cardNoticeBar(BuildContext context) { | direction | Axis? | Axis.horizontal | 滚动方向 | | height | double | 22 | 文字高度 (当使用prefixIcon或suffixIcon时,icon大小值等于该属性) | | interval | int? | 3000 | 步进滚动间隔时间(毫秒) | -| key | | - | | +| key | Key? | - | 组件标识,用于区分或保留组件状态。 | | left | Widget? | - | 左侧内容(自定义左侧内容,优先级高于prefixIcon) | | marquee | bool? | false | 跑马灯效果 | | maxLines | int? | 1 | 文本行数(仅静态有效) | @@ -309,8 +307,6 @@ Widget _cardNoticeBar(BuildContext context) { | suffixIcon | IconData? | - | 右侧图标 | | theme | TNoticeBarTheme? | TNoticeBarTheme.info | 主题 | -``` -``` ### TNoticeBarStyle #### 简介 @@ -329,9 +325,41 @@ Widget _cardNoticeBar(BuildContext context) { #### 工厂构造方法 -| 名称 | 说明 | -| --- | --- | -| TNoticeBarStyle.generateTheme | 根据主题生成样式 | +##### TNoticeBarStyle.generateTheme + +根据主题生成样式 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| context | BuildContext | - | 上下文 | +| theme | TNoticeBarTheme? | TNoticeBarTheme.info | - | + + +### TNoticeBarType +#### 简介 +公告栏类型 +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| none | 静止(默认) | +| scroll | 滚动 | +| step | 步进 | + + +### TNoticeBarTheme +#### 简介 +公告栏主题 +#### 枚举值 + + +| 名称 | 说明 | +| --- | --- | +| info | 信息(默认) | +| success | 成功 | +| warning | 警告 | +| error | 错误 | \ No newline at end of file diff --git a/tdesign-site/src/picker/README.md b/tdesign-site/src/picker/README.md index 2dfa16264..f061a540b 100644 --- a/tdesign-site/src/picker/README.md +++ b/tdesign-site/src/picker/README.md @@ -30,11 +30,13 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('选中城市: ${selectedCity.isEmpty ? "未选择" : selectedCity}', - style: TextStyle(fontSize: 14, color: TTheme.of(context).textColorSecondary)), + style: TextStyle( + fontSize: 14, color: TTheme.of(context).textColorSecondary)), const SizedBox(height: 8), _pickerCard( context, - child: TPicker(items: cityItems, + child: TPicker( + items: cityItems, onChange: (v) => setState(() => selectedCity = v.labels.first)), ), ], @@ -54,13 +56,16 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('选中时间: ${selectedTime.isEmpty ? "未选择" : selectedTime}', - style: TextStyle(fontSize: 14, color: TTheme.of(context).textColorSecondary)), + style: TextStyle( + fontSize: 14, color: TTheme.of(context).textColorSecondary)), const SizedBox(height: 8), _pickerCard( context, - child: TPicker(items: timeItems, itemCount: 5, - onChange: (v) => setState(() => - selectedTime = '${v.values[0]}:${v.values[1].toString().padLeft(2, '0')}:${v.values[2].toString().padLeft(2, '0')}')), + child: TPicker( + items: timeItems, + itemCount: 5, + onChange: (v) => setState(() => selectedTime = + '${v.values[0]}:${v.values[1].toString().padLeft(2, '0')}:${v.values[2].toString().padLeft(2, '0')}')), ), ], ); @@ -79,12 +84,16 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('选中地区: ${selectedLinked.isEmpty ? "未选择" : selectedLinked}', - style: TextStyle(fontSize: 14, color: TTheme.of(context).textColorSecondary)), + style: TextStyle( + fontSize: 14, color: TTheme.of(context).textColorSecondary)), const SizedBox(height: 8), _pickerCard( context, - child: TPicker(items: linkedItems, initialValue: const ['GD', 'SZ', 'NS'], - onChange: (v) => setState(() => selectedLinked = v.labels.join(' / '))), + child: TPicker( + items: linkedItems, + initialValue: const ['GD', 'SZ', 'NS'], + onChange: (v) => + setState(() => selectedLinked = v.labels.join(' / '))), ), ], ); @@ -178,15 +187,20 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('选中: ${selectedItemDisabled.isEmpty ? "未选择" : selectedItemDisabled}', - style: TextStyle(fontSize: 14, color: TTheme.of(context).textColorSecondary)), + Text( + '选中: ${selectedItemDisabled.isEmpty ? "未选择" : selectedItemDisabled}', + style: TextStyle( + fontSize: 14, color: TTheme.of(context).textColorSecondary)), const SizedBox(height: 4), Text('提示: 标灰的选项不可选(第1列「保密」、第2列「A排1座/A排6座/A排7座/A排8座/A排12座」)', - style: TextStyle(fontSize: 12, color: TTheme.of(context).textColorPlaceholder)), + style: TextStyle( + fontSize: 12, color: TTheme.of(context).textColorPlaceholder)), const SizedBox(height: 8), _pickerCard( context, - child: TPicker(items: itemDisabledItems, initialValue: const ['M', 'A5'], + child: TPicker( + items: itemDisabledItems, + initialValue: const ['M', 'A5'], onChange: (v) => setState(() => selectedItemDisabled = '${v.labels.first} ${v.labels.last}')), ), @@ -224,13 +238,16 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; const SizedBox(height: 8), _pickerCard( context, - child: TPicker(items: cityItems, initialValue: const ['GZ'], + child: TPicker( + items: cityItems, + initialValue: const ['GZ'], onChange: (v) => debugPrint('选中: $v'), disabled: globalDisabled), ), const SizedBox(height: 4), Text('切换开关可控制整个选择器的禁用/启用状态', - style: TextStyle(fontSize: 12, color: TTheme.of(context).textColorPlaceholder)), + style: TextStyle( + fontSize: 12, color: TTheme.of(context).textColorPlaceholder)), ], ); } @@ -327,8 +344,7 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; titleWidget: Row( mainAxisSize: MainAxisSize.min, children: [ - Icon(TIcons.location, - size: 18, color: theme.brandNormalColor), + Icon(TIcons.location, size: 18, color: theme.brandNormalColor), const SizedBox(width: 4), Text('选择地区', style: TextStyle( @@ -339,7 +355,8 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; ], ), cancel: Icon(TIcons.close, size: 22, color: theme.fontGyColor2), - confirm: Icon(TIcons.check, size: 22, color: theme.brandNormalColor), + confirm: + Icon(TIcons.check, size: 22, color: theme.brandNormalColor), ), ), ], @@ -357,19 +374,22 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
   Widget buildCustomKeys(BuildContext context) {
     // 用 keys 告诉组件「city 映射为 label,code 是 value,readonly 是 disabled」
-    const keys = TPickerKeys(label: 'city', value: 'code', disabled: 'readonly');
+    const keys =
+        TPickerKeys(label: 'city', value: 'code', disabled: 'readonly');
     final label = _customKeysValue?.labels.join() ?? '';
     return Column(
       crossAxisAlignment: CrossAxisAlignment.start,
       children: [
         Text(
           '后端原始字段:city / code / readonly。通过 keys(label: "city") 映射为 label',
-          style: TextStyle(fontSize: 12, color: TTheme.of(context).textColorPlaceholder),
+          style: TextStyle(
+              fontSize: 12, color: TTheme.of(context).textColorPlaceholder),
         ),
         const SizedBox(height: 4),
         Text(
           '当前选中:${label.isEmpty ? "未选择" : label}',
-          style: TextStyle(fontSize: 14, color: TTheme.of(context).textColorSecondary),
+          style: TextStyle(
+              fontSize: 14, color: TTheme.of(context).textColorSecondary),
         ),
         const SizedBox(height: 8),
         _pickerCard(
@@ -399,7 +419,8 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
       children: [
         Text(
           '示例:height(300) + itemCount(7),每屏显示 7 项',
-          style: TextStyle(fontSize: 12, color: TTheme.of(context).textColorPlaceholder),
+          style: TextStyle(
+              fontSize: 12, color: TTheme.of(context).textColorPlaceholder),
         ),
         const SizedBox(height: 8),
         _pickerCard(
@@ -429,12 +450,14 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
       children: [
         Text(
           '示例:itemBuilder 自定义子项渲染,可添加图标、背景色等',
-          style: TextStyle(fontSize: 12, color: TTheme.of(context).textColorPlaceholder),
+          style: TextStyle(
+              fontSize: 12, color: TTheme.of(context).textColorPlaceholder),
         ),
         const SizedBox(height: 4),
         Text(
           '选中: ${_customItemBuilderValue.isEmpty ? "未选择" : _customItemBuilderValue}',
-          style: TextStyle(fontSize: 14, color: TTheme.of(context).textColorSecondary),
+          style: TextStyle(
+              fontSize: 14, color: TTheme.of(context).textColorSecondary),
         ),
         const SizedBox(height: 8),
         _pickerCard(
@@ -460,15 +483,19 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
                       content,
                       style: TextStyle(
                         fontSize: 16,
-                        fontWeight: selected ? FontWeight.w600 : FontWeight.normal,
-                        color: selected ? theme.brandNormalColor : theme.fontGyColor1,
+                        fontWeight:
+                            selected ? FontWeight.w600 : FontWeight.normal,
+                        color: selected
+                            ? theme.brandNormalColor
+                            : theme.fontGyColor1,
                       ),
                     ),
                   ],
                 ),
               );
             },
-            onChange: (v) => setState(() => _customItemBuilderValue = v.labels.first),
+            onChange: (v) =>
+                setState(() => _customItemBuilderValue = v.labels.first),
           ),
         ),
       ],
@@ -485,25 +512,23 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 
 | 参数 | 类型 | 默认值 | 说明 |
 | --- | --- | --- | --- |
-| cancel | Widget? | - | 工具栏左侧自定义插槽,默认使用 [TResourceDelegate.cancel] |
-| confirm | Widget? | - | 工具栏右侧自定义插槽,默认使用 [TResourceDelegate.confirm] |
+| cancel | Widget? | - | 工具栏左侧自定义插槽,默认使用 [TResourceDelegate.cancel] 可用于渲染图标、图标+文字组合等。点击事件依然由外层 [GestureDetector] 处理,触发 [onCancel] 回调——所以插槽内的 Widget 不需要自己处理点击。 ```dart // 简单改文字 TPicker( cancel: const Text('关闭'), onCancel: () => Navigator.of(context).pop(), ) // 带图标 TPicker( cancel: const Icon(Icons.close, size: 22), onCancel: () => Navigator.of(context).pop(), ) ``` |
+| confirm | Widget? | - | 工具栏右侧自定义插槽,默认使用 [TResourceDelegate.confirm] 可用于渲染图标、图标+文字组合等。点击事件依然由外层 [GestureDetector] 处理,触发 [onConfirm] 回调——所以插槽内的 Widget 不需要自己处理点击。 ```dart // 简单改文字 TPicker( confirm: const Text('确定'), onConfirm: (v) => Navigator.of(context).pop(v), ) // 带图标 TPicker( confirm: const Icon(Icons.check, size: 22), onConfirm: (v) => Navigator.of(context).pop(v), ) ``` |
 | disabled | bool | false | 是否禁用整个选择器(禁止滚动和操作),默认 false |
 | height | double | 200 | 视窗高度,默认 200 |
 | initialValue | List? | - | 初始选中值列表(按 value 匹配) |
 | itemBuilder | ItemBuilderType? | - | 自定义子项构建器(disabled 项仍由内部统一渲染,不会走此 builder) |
 | itemCount | int | 5 | 每屏显示 item 数,默认 5 |
 | itemDistanceCalculator | ItemDistanceCalculator? | - | 自定义距离计算器(控制颜色/字重/字号随"离中心距离"的变化) |
-| items | TPickerItems | - | 数据源(必填) |
-| key |  | - |  |
-| onCancel | VoidCallback? | - | 点击「取消」按钮回调 |
-| onChange | void Function(TPickerValue)? | - | 值改变回调(滚动时实时触发) |
-| onConfirm | void Function(TPickerValue)? | - | 点击「确认」按钮回调 |
-| onLoad | void Function(TPickerLoadEvent)? | - | 列选中项变化的事件回调 |
-| title | String? | - | 工具栏中部标题(可选,不传时中部留白) |
-| titleWidget | Widget? | - | 工具栏中部自定义标题插槽 |
+| items | TPickerItems | - | 数据源(必填) 使用密封类 [TPickerItems] 编译期强制二选一: - [TPickerColumns] → 多列独立选择 - [TPickerLinked] → 联动选择 自由结构数据通过 `.fromRaw()` 工厂构造归一化。 |
+| key | Key? | - | 组件标识,用于区分或保留组件状态。 |
+| onCancel | VoidCallback? | - | 点击「取消」按钮回调 仅作为点击事件通知,不携带任何参数。组件本身不会做任何 popup 操作,业务层可在此自行决定是否关闭弹窗、重置状态等。 |
+| onChange | void Function(TPickerValue)? | - | 值改变回调(滚动时实时触发) 触发时机: - 用户滚动经过某个 enabled 项并稳定时 - disabled 修正动画完成后,回调最终落点 **注意**:此回调代表"滚动时实时变化",不代表"用户已确认选择"。 如需"已确认"语义,请使用 [onConfirm]。 如需做网络请求/埋点等去抖处理,请在业务层自行 debounce。 |
+| onConfirm | void Function(TPickerValue)? | - | 点击「确认」按钮回调 携带当前选中的完整 [TPickerValue],包含: - `selectedOptions`: 当前选中的所有 [TPickerOption] - `values`: 各列选中项的 value 列表 - `labels`: 各列选中项的 label 列表 - `indexes`: 各列选中项的索引 与 [onChange] 不同——只有用户点击「确认」时才触发,代表"已确认选择"。 组件本身不会做任何 popup 操作,业务层可在此自行决定是否关闭弹窗、 提交表单等。 |
+| onLoad | void Function(TPickerLoadEvent)? | - | 列选中项变化的事件回调 **触发时机**:每次用户滚动到一个 enabled 项后都会触发(联动模式下还会 在新展开的列就位后触发)。组件本身不做"距底部多少项"的阈值判断——把 决策权交给业务层。 **事件参数**包含: - [TPickerLoadEvent.column]:触发列索引 - [TPickerLoadEvent.remaining]:当前列距底部剩余项数 - [TPickerLoadEvent.displayedCount]:当前列总项数 - [TPickerLoadEvent.parentValue]:联动模式下父级选中值(首列为 null) **典型用法**:业务层根据 [TPickerLoadEvent.remaining] 自行判断是否加载更多。 ```dart onLoad: (e) async { if (e.remaining > 5 \|\| _isLoading) return; // 距底部还远 / 已在加载,跳过 _isLoading = true; final more = await fetchMore(parent: e.parentValue); setState(() { _data.addAll(more); _isLoading = false; }); } ``` |
+| title | String? | - | 工具栏中部标题(可选,不传时中部留白) 顶部工具栏永远显示,包含「取消」「标题」「确认」三块。 用户点击「取消」触发 [onCancel],点击「确认」触发 [onConfirm]。 选择器与弹窗(popup)完全解耦——关闭/打开弹窗的逻辑由业务层在 这两个回调中自行控制。 典型用法(与 popup 弹窗组合): ```dart TPicker( items: items, title: '请选择地区', onCancel: () => setState(() => visible = false), onConfirm: (value) { setState(() { selected = value; visible = false; }); }, ) ``` |
+| titleWidget | Widget? | - | 工具栏中部自定义标题插槽 传入后会**完全替换**默认的 [title] 文字,可用于渲染更复杂的标题(副标题、图标+文字等)。 标题区域不响应点击。 |
 
-```
-```
 
 ### TPickerOption
 #### 默认构造方法
@@ -514,8 +539,6 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 | label | String | - | 展示文字(可包含 emoji、单位、国际化等) |
 | value | dynamic | - | 业务值(onChange 回调返回此字段) |
 
-```
-```
 
 ### TPickerValue
 #### 默认构造方法
@@ -525,8 +548,13 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 | indexes | List | - | 每列选中项的索引 |
 | selectedOptions | List | - | 每列选中的完整 option |
 
-```
-```
+#### 公开属性
+
+| 属性 | 类型 | 默认值 | 说明 |
+| --- | --- | --- | --- |
+| labels | List | - | 所有选中项的 label(展示用) 顺序与列顺序对应,可直接用于 UI 展示。 懒计算并缓存,生命周期内只计算一次。 |
+| values | List | - | 所有选中项的 value(提交表单用) 顺序与列顺序对应,可直接用于表单提交。 懒计算并缓存,生命周期内只计算一次。 |
+
 
 ### TPickerLoadEvent
 #### 默认构造方法
@@ -535,63 +563,71 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 | --- | --- | --- | --- |
 | column | int | - | 触发事件的列索引(0 表示第一列) |
 | displayedCount | int | - | 当前列已展示的选项总数 |
-| parentValue | dynamic | - | 当前列的父级选中值(联动模式下使用) |
+| parentValue | dynamic | - | 当前列的父级选中值(联动模式下使用) 第一列时为 null;业务层可用此值从原始数据中筛选子级选项。 |
 | remaining | int | - | 距底部剩余的选项数(业务可用此值做"接近底部时加载"判断) |
 
-```
-```
 
 ### TPickerColumns
 #### 默认构造方法
 
 | 参数 | 类型 | 默认值 | 说明 |
 | --- | --- | --- | --- |
-| columns | List> | columns | 每列的选项列表 |
+| columns | List> | - | 每列的选项列表 |
 
 
 #### 工厂构造方法
 
-| 名称  | 说明 |
-| --- |  --- |
-| TPickerColumns.fromRaw  | 从自由结构的 raw 数据创建,自动归一化
+##### TPickerColumns.fromRaw
+
+从自由结构的 raw 数据创建,自动归一化
 
  ```dart
  TPickerColumns.fromRaw(
    [['北京', '上海', '广州']],
    keys: const TPickerKeys(label: 'name', value: 'code'),
  )
- ``` |
+ ```
+
+| 参数 | 类型 | 默认值 | 说明 |
+| --- | --- | --- | --- |
+| rawColumns | List | - | - |
+| keys | TPickerKeys | TPickerKeys.defaults | - |
 
-```
-```
 
 ### TPickerLinked
 #### 默认构造方法
 
 | 参数 | 类型 | 默认值 | 说明 |
 | --- | --- | --- | --- |
-| tree | Map | tree | 联动树结构:`Map` |
+| tree | Map | - | 联动树结构:`Map` value 可以是: - `Map` → 下一级联动 - `List` → 叶子级选项 |
 
 
 #### 工厂构造方法
 
-| 名称  | 说明 |
-| --- |  --- |
-| TPickerLinked.fromRaw  | 从自由结构的 raw Map 数据创建,自动归一化
+##### TPickerLinked.fromRaw
+
+从自由结构的 raw Map 数据创建,自动归一化
 
  ```dart
  TPickerLinked.fromRaw({
    '广东': {'深圳': ['南山', '福田'], '广州': ['天河']},
    '浙江': {'杭州': ['西湖']},
  })
- ``` |
+ ```
+
+| 参数 | 类型 | 默认值 | 说明 |
+| --- | --- | --- | --- |
+| rawTree | Map | - | - |
+| keys | TPickerKeys | TPickerKeys.defaults | - |
 
-```
-```
 
 ### TPickerItems
-```
-```
+#### 简介
+选择器数据源密封类
+
+ 编译期强制二选一,消除运行时类型错误:
+ - [TPickerColumns] → 多列独立选择
+ - [TPickerLinked] → 联动选择
 
 ### TPickerKeys
 #### 默认构造方法
@@ -603,5 +639,11 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 | label | String | 'label' | 展示文案对应的字段名,默认 `label` |
 | value | String | 'value' | 业务值对应的字段名,默认 `value` |
 
+#### 静态成员
+
+| 名称 | 类型 | 默认值 | 说明 |
+| --- | --- | --- | --- |
+| defaults | TPickerKeys | - | 默认配置(`label / value / disabled / children`) |
+
 
   
\ No newline at end of file
diff --git a/tdesign-site/src/popover/README.md b/tdesign-site/src/popover/README.md
index 60e8283db..aae1a64a0 100644
--- a/tdesign-site/src/popover/README.md
+++ b/tdesign-site/src/popover/README.md
@@ -1276,21 +1276,34 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 
 ## API
 ### TPopover
-#### 简介
-
 
 #### 静态方法
 
-| 名称 | 返回类型 | 参数 | 说明 |
+##### TPopover.showPopover
+
+返回类型:`Future`
+
+| 参数 | 类型 | 默认值 | 说明 |
 | --- | --- | --- | --- |
-| showPopover |  |   required BuildContext context,  String? content,  Widget? contentWidget,  double offset,  TPopoverTheme? theme,  bool closeOnClickOutside,  TPopoverPlacement? placement,  bool? showArrow,  double arrowSize,  EdgeInsetsGeometry? padding,  double? width,  double? height,  Color? overlayColor,  OnTap? onTap,  OnLongTap? onLongTap,  BorderRadius? radius, |  |
+| context | BuildContext | - | 上下文 |
+| content | String? | - | 显示内容 |
+| contentWidget | Widget? | - | 自定义内容 |
+| offset | double | 4 | 偏移 |
+| theme | TPopoverTheme? | - | 弹出气泡主题 |
+| closeOnClickOutside | bool | true | - |
+| placement | TPopoverPlacement? | - | 浮层出现位置 |
+| showArrow | bool? | true | 是否显示浮层箭头 |
+| arrowSize | double | 8 | 箭头大小 |
+| padding | EdgeInsetsGeometry? | - | 内容内边距 |
+| width | double? | - | 内容宽度(包含padding,实际高度:height - paddingLeft - paddingRight) |
+| height | double? | - | 内容高度(包含padding,实际高度:height - paddingTop - paddingBottom) |
+| overlayColor | Color? | Colors.transparent | - |
+| onTap | OnTap? | - | 点击事件 |
+| onLongTap | OnLongTap? | - | 长按事件 |
+| radius | BorderRadius? | - | 圆角 |
 
-```
-```
 
 ### TPopoverWidget
-#### 简介
-
 #### 默认构造方法
 
 | 参数 | 类型 | 默认值 | 说明 |
@@ -1300,7 +1313,7 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 | contentWidget | Widget? | - | 自定义内容 |
 | context | BuildContext | - | 上下文 |
 | height | double? | - | 内容高度(包含padding,实际高度:height - paddingTop - paddingBottom) |
-| key |  | - |  |
+| key | Key? | - | 组件标识,用于区分或保留组件状态。 |
 | offset | double | 4 | 偏移 |
 | onLongTap | OnLongTap? | - | 长按事件 |
 | onTap | OnTap? | - | 点击事件 |
@@ -1312,4 +1325,54 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 | width | double? | - | 内容宽度(包含padding,实际高度:height - paddingLeft - paddingRight) |
 
 
+### TPopoverTheme
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| dark | 暗色 |
+| light | 亮色 |
+| info | 品牌色 |
+| success | 成功 |
+| warning | 警告 |
+| error | 错误 |
+
+
+### TPopoverPlacement
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| topLeft | 上左 |
+| top | 上 |
+| topRight | 上右 |
+| rightTop | 右上 |
+| right | 右 |
+| rightBottom | 右下 |
+| bottomRight | 下右 |
+| bottom | 下 |
+| bottomLeft | 下左 |
+| leftBottom | 左下 |
+| left | 左 |
+| leftTop | 左上 |
+
+
+### OnTap
+#### 类型定义
+
+```dart
+typedef OnTap =  Function(String? content);
+```
+
+
+### OnLongTap
+#### 类型定义
+
+```dart
+typedef OnLongTap =  Function(String? content);
+```
+
+
   
\ No newline at end of file
diff --git a/tdesign-site/src/popup/README.md b/tdesign-site/src/popup/README.md
index ec55fb953..b9588e4f2 100644
--- a/tdesign-site/src/popup/README.md
+++ b/tdesign-site/src/popup/README.md
@@ -33,21 +33,16 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
       type: TButtonType.outline,
       size: TButtonSize.large,
       onTap: () {
-        Navigator.of(context).push(
-          TSlidePopupRoute(
-              slideTransitionFrom: SlideTransitionFrom.top,
-              open: () {
-                print('open');
-              },
-              opened: () {
-                print('opened');
-              },
-              builder: (context) {
-                return Container(
-                  color: TTheme.of(context).bgColorContainer,
-                  height: 240,
-                );
-              }),
+        TPopup.show(
+          context,
+          options: TPopupOptions.top(
+              height: 240,
+              onOpen: () => print('open'),
+              onOpened: () => print('opened'),
+              child: Container(
+                color: TTheme.of(context).bgColorContainer,
+                height: 240,
+              )),
         );
       },
     );
@@ -69,15 +64,13 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
       type: TButtonType.outline,
       size: TButtonSize.large,
       onTap: () {
-        Navigator.of(context).push(
-          TSlidePopupRoute(
-              slideTransitionFrom: SlideTransitionFrom.left,
-              builder: (context) {
-                return Container(
-                  color: TTheme.of(context).bgColorContainer,
-                  width: 280,
-                );
-              }),
+        TPopup.show(
+          context,
+          options: TPopupOptions.left(
+              width: 280,
+              child: Container(
+                color: TTheme.of(context).bgColorContainer,
+              )),
         );
       },
     );
@@ -99,20 +92,19 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
       type: TButtonType.outline,
       size: TButtonSize.large,
       onTap: () {
-        Navigator.of(context).push(
-          TSlidePopupRoute(
-              slideTransitionFrom: SlideTransitionFrom.center,
-              builder: (context) {
-                return Container(
-                  decoration: BoxDecoration(
-                    color: TTheme.of(context).bgColorContainer,
-                    borderRadius:
-                        BorderRadius.circular(TTheme.of(context).radiusLarge),
-                  ),
-                  width: 240,
-                  height: 240,
-                );
-              }),
+        TPopup.show(
+          context,
+          options: TPopupOptions.center(
+              closeBuilder: null,
+              child: Container(
+                decoration: BoxDecoration(
+                  color: TTheme.of(context).bgColorContainer,
+                  borderRadius:
+                      BorderRadius.circular(TTheme.of(context).radiusLarge),
+                ),
+                width: 240,
+                height: 240,
+              )),
         );
       },
     );
@@ -134,15 +126,15 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
       type: TButtonType.outline,
       size: TButtonSize.large,
       onTap: () {
-        Navigator.of(context).push(
-          TSlidePopupRoute(
-              slideTransitionFrom: SlideTransitionFrom.bottom,
-              builder: (context) {
-                return Container(
-                  color: TTheme.of(context).bgColorContainer,
-                  height: 240,
-                );
-              }),
+        TPopup.show(
+          context,
+          options: TPopupOptions.bottom(
+              height: 240,
+              headerBuilder: null,
+              child: Container(
+                color: TTheme.of(context).bgColorContainer,
+                height: 240,
+              )),
         );
       },
     );
@@ -164,15 +156,13 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
       type: TButtonType.outline,
       size: TButtonSize.large,
       onTap: () {
-        Navigator.of(context).push(
-          TSlidePopupRoute(
-              slideTransitionFrom: SlideTransitionFrom.right,
-              builder: (context) {
-                return Container(
-                  color: TTheme.of(context).bgColorContainer,
-                  width: 280,
-                );
-              }),
+        TPopup.show(
+          context,
+          options: TPopupOptions.right(
+              width: 280,
+              child: Container(
+                color: TTheme.of(context).bgColorContainer,
+              )),
         );
       },
     );
@@ -195,23 +185,12 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
       type: TButtonType.outline,
       size: TButtonSize.large,
       onTap: () {
-        Navigator.of(context).push(
-          TSlidePopupRoute(
-            slideTransitionFrom: SlideTransitionFrom.bottom,
-            builder: (context) {
-              return TPopupBottomConfirmPanel(
-                title: '标题文字',
-                leftClick: () {
-                  Navigator.maybePop(context);
-                },
-                rightClick: () {
-                  TToast.showText('确定', context: context);
-                  Navigator.maybePop(context);
-                },
-                child: Container(height: 200),
-              );
-            },
-          ),
+        TPopup.show(
+          context,
+          options: TPopupOptions.bottom(
+              height: 280,
+              titleWidget: TText('标题文字'),
+              child: Container(height: 200)),
         );
       },
     );
@@ -225,31 +204,58 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 
 
   
-  Widget _buildPopFromBottomWithOperation(BuildContext context) {
+  Widget _buildPopFromBottomWithCloseAndTitle(BuildContext context) {
     return TButton(
-      text: '底部弹出层-带操作',
+      text: '底部弹出层-带标题及关闭',
       isBlock: true,
       theme: TButtonTheme.primary,
       type: TButtonType.outline,
       size: TButtonSize.large,
       onTap: () {
-        Navigator.of(context).push(TSlidePopupRoute(
-            modalBarrierColor: TTheme.of(context).fontGyColor2,
-            slideTransitionFrom: SlideTransitionFrom.bottom,
-            builder: (context) {
-              return TPopupBottomConfirmPanel(
-                leftClick: () {
-                  Navigator.maybePop(context);
-                },
-                rightClick: () {
-                  TToast.showText('确定', context: context);
-                  Navigator.maybePop(context);
-                },
-                child: Container(
-                  height: 200,
-                ),
-              );
-            }));
+        TPopup.show(
+          context,
+          options: TPopupOptions.bottom(
+              height: 280,
+              cancelBuilder: (_, close) => GestureDetector(
+                    behavior: HitTestBehavior.opaque,
+                    onTap: close,
+                    child: Padding(
+                      padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 8),
+                      child: TText(
+                        '关闭',
+                        textColor: TTheme.of(context).textColorSecondary,
+                        font: TTheme.of(context).fontBodyLarge,
+                      ),
+                    ),
+                  ),
+              titleWidget: Row(
+                mainAxisSize: MainAxisSize.min,
+                children: [
+                  Icon(TIcons.info_circle,
+                      color: TTheme.of(context).brandNormalColor, size: 18),
+                  const SizedBox(width: 4),
+                  TText(
+                    '自定义标题',
+                    textColor: TTheme.of(context).brandNormalColor,
+                    font: TTheme.of(context).fontTitleMedium,
+                  ),
+                ],
+              ),
+              confirmBuilder: (_, close) => GestureDetector(
+                    behavior: HitTestBehavior.opaque,
+                    onTap: close,
+                    child: Padding(
+                      padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 8),
+                      child: TText(
+                        '完成',
+                        textColor: TTheme.of(context).brandNormalColor,
+                        font: TTheme.of(context).fontTitleMedium,
+                        fontWeight: FontWeight.w600,
+                      ),
+                    ),
+                  ),
+              child: Container(height: 200)),
+        );
       },
     );
   }
@@ -262,26 +268,29 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
-  Widget _buildPopFromBottomWithCloseAndTitle(BuildContext context) {
+  Widget _buildPopFromCenterWithClose(BuildContext context) {
     return TButton(
-      text: '底部弹出层-带标题及关闭',
+      text: '居中弹出层-带关闭',
       isBlock: true,
       theme: TButtonTheme.primary,
       type: TButtonType.outline,
       size: TButtonSize.large,
       onTap: () {
-        Navigator.of(context).push(
-          TSlidePopupRoute(
-              slideTransitionFrom: SlideTransitionFrom.bottom,
-              builder: (context) {
-                return TPopupBottomDisplayPanel(
-                  title: '标题文字',
-                  closeClick: () {
-                    Navigator.maybePop(context);
-                  },
-                  child: Container(height: 200),
-                );
-              }),
+        TPopup.show(
+          context,
+          options: TPopupOptions.center(
+              closeOnOverlayClick: false,
+              width: 240,
+              height: 240,
+              closeBuilder: (_, close) => IconButton(
+                    icon: Icon(
+                      TIcons.close_circle,
+                      color: TTheme.of(context).fontWhColor1,
+                      size: 32,
+                    ),
+                    onPressed: close,
+                  ),
+              child: const SizedBox(width: 240, height: 240)),
         );
       },
     );
@@ -295,27 +304,33 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 
 
   
-  Widget _buildPopFromBottomWithCloseAndLeftTitle(BuildContext context) {
+  Widget _buildPopFromCenterWithUnderClose(BuildContext context) {
     return TButton(
-      text: '底部弹出层-带左边标题及关闭',
+      text: '居中弹出层-自定义下方按钮',
       isBlock: true,
       theme: TButtonTheme.primary,
       type: TButtonType.outline,
       size: TButtonSize.large,
       onTap: () {
-        Navigator.of(context).push(
-          TSlidePopupRoute(
-              slideTransitionFrom: SlideTransitionFrom.bottom,
-              builder: (context) {
-                return TPopupBottomDisplayPanel(
-                  title: '标题文字',
-                  titleLeft: true,
-                  closeClick: () {
-                    Navigator.maybePop(context);
-                  },
-                  child: Container(height: 200),
-                );
-              }),
+        TPopup.show(
+          context,
+          options: TPopupOptions.center(
+              closeOnOverlayClick: true,
+              width: 240,
+              height: 200,
+              closeBuilder: (_, close) => IconButton(
+                    icon: Icon(
+                      TIcons.poweroff,
+                      color: TTheme.of(context).fontWhColor1,
+                      size: 36,
+                    ),
+                    onPressed: close,
+                  ),
+              child: Container(
+                width: 240,
+                height: 200,
+                color: TTheme.of(context).bgColorContainer,
+              )),
         );
       },
     );
@@ -329,25 +344,65 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 
 
   
-  Widget _buildPopFromBottomWithClose(BuildContext context) {
+  Widget _buildNestedPopup(BuildContext context) {
     return TButton(
-      text: '底部弹出层-带关闭',
+      text: '内层再弹一层(嵌套叠加)',
       isBlock: true,
       theme: TButtonTheme.primary,
       type: TButtonType.outline,
       size: TButtonSize.large,
       onTap: () {
-        Navigator.of(context).push(
-          TSlidePopupRoute(
-              slideTransitionFrom: SlideTransitionFrom.bottom,
-              builder: (context) {
-                return TPopupBottomDisplayPanel(
-                  closeClick: () {
-                    Navigator.maybePop(context);
-                  },
-                  child: Container(height: 200),
-                );
-              }),
+        TPopupHandle? outerHandle;
+        outerHandle = TPopup.show(
+          context,
+          options: TPopupOptions.bottom(
+              height: 360,
+              headerBuilder: null,
+              child: Builder(
+                builder: (innerContext) {
+                  return Padding(
+                    padding: const EdgeInsets.all(16),
+                    child: Column(
+                      crossAxisAlignment: CrossAxisAlignment.stretch,
+                      children: [
+                        TText(
+                          '外层:headerBuilder: null,仅 child',
+                          textColor: TTheme.of(innerContext).textColorSecondary,
+                        ),
+                        const SizedBox(height: 16),
+                        TButton(
+                          text: '打开内层 Popup',
+                          isBlock: true,
+                          theme: TButtonTheme.primary,
+                          size: TButtonSize.large,
+                          onTap: () {
+                            TPopup.show(
+                              innerContext,
+                              options: TPopupOptions.bottom(
+                                height: 280,
+                                titleWidget: const TText('内层标题'),
+                                child: Container(
+                                  height: 160,
+                                  color: TTheme.of(innerContext)
+                                      .bgColorSecondaryContainer,
+                                ),
+                              ),
+                            );
+                          },
+                        ),
+                        const SizedBox(height: 12),
+                        TButton(
+                          text: '关闭外层',
+                          isBlock: true,
+                          type: TButtonType.outline,
+                          size: TButtonSize.large,
+                          onTap: () => outerHandle?.close(),
+                        ),
+                      ],
+                    ),
+                  );
+                },
+              )),
         );
       },
     );
@@ -355,33 +410,31 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 
 
                                   
+### 1 更多 API
 
 
             
 
 
   
-  Widget _buildPopFromBottomWithTitle(BuildContext context) {
+  Widget _buildApiInset(BuildContext context) {
     return TButton(
-      text: '底部弹出层-仅标题',
+      text: 'bottom inset.left/right',
       isBlock: true,
       theme: TButtonTheme.primary,
       type: TButtonType.outline,
       size: TButtonSize.large,
       onTap: () {
-        Navigator.of(context).push(
-          TSlidePopupRoute(
-              slideTransitionFrom: SlideTransitionFrom.bottom,
-              builder: (context) {
-                return TPopupBottomDisplayPanel(
-                  title: '标题文字',
-                  hideClose: true,
-                  // closeClick: () {
-                  //   Navigator.maybePop(context);
-                  // },
-                  child: Container(height: 200),
-                );
-              }),
+        TPopup.show(
+          context,
+          options: TPopupOptions.bottom(
+              height: 320,
+              inset: const TPopupBottomInset(left: 16, right: 16),
+              titleWidget: TText('左右留白'),
+              child: Container(
+                height: 240,
+                color: TTheme.of(context).bgColorContainer,
+              )),
         );
       },
     );
@@ -395,26 +448,26 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 
 
   
-  Widget _buildPopFromCenterWithClose(BuildContext context) {
+  Widget _buildApiShowOverlayFalse(BuildContext context) {
     return TButton(
-      text: '居中弹出层-带关闭',
+      text: 'showOverlay: false(透明模态)',
       isBlock: true,
       theme: TButtonTheme.primary,
       type: TButtonType.outline,
       size: TButtonSize.large,
       onTap: () {
-        Navigator.of(context).push(
-          TSlidePopupRoute(
-              isDismissible: false,
-              slideTransitionFrom: SlideTransitionFrom.center,
-              builder: (context) {
-                return TPopupCenterPanel(
-                  closeClick: () {
-                    Navigator.maybePop(context);
-                  },
-                  child: const SizedBox(width: 240, height: 240),
-                );
-              }),
+        TPopup.show(
+          context,
+          options: TPopupOptions.bottom(
+              height: 280,
+              showOverlay: false,
+              modal: true,
+              // 不显示可见蒙层,但仍阻断背景交互;须保留其它关闭入口。
+              titleWidget: const TText('透明模态'),
+              child: Container(
+                height: 200,
+                color: TTheme.of(context).bgColorContainer,
+              )),
         );
       },
     );
@@ -428,27 +481,53 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 
 
   
-  Widget _buildPopFromCenterWithUnderClose(BuildContext context) {
+  Widget _buildApiOnOverlayClick(BuildContext context) {
+    return TButton(
+      text: 'onOverlayClick',
+      isBlock: true,
+      theme: TButtonTheme.primary,
+      type: TButtonType.outline,
+      size: TButtonSize.large,
+      onTap: () {
+        TPopup.show(
+          context,
+          options: TPopupOptions.bottom(
+              height: 260,
+              onOverlayClick: () => TToast.showText('点击蒙层', context: context),
+              child: Container(
+                height: 200,
+                color: TTheme.of(context).bgColorContainer,
+              )),
+        );
+      },
+    );
+  }
+ +
+ + + + + + +
+  Widget _buildApiDuration(BuildContext context) {
     return TButton(
-      text: '居中弹出层-关闭在下方',
+      text: 'animationDuration: 600ms',
       isBlock: true,
       theme: TButtonTheme.primary,
       type: TButtonType.outline,
       size: TButtonSize.large,
       onTap: () {
-        Navigator.of(context).push(
-          TSlidePopupRoute(
-              isDismissible: false,
-              slideTransitionFrom: SlideTransitionFrom.center,
-              builder: (context) {
-                return TPopupCenterPanel(
-                  closeUnderBottom: true,
-                  closeClick: () {
-                    Navigator.maybePop(context);
-                  },
-                  child: const SizedBox(width: 240, height: 240),
-                );
-              }),
+        TPopup.show(
+          context,
+          options: TPopupOptions.bottom(
+              height: 240,
+              animationDuration: const Duration(milliseconds: 600),
+              child: Container(
+                height: 200,
+                color: TTheme.of(context).bgColorContainer,
+              )),
         );
       },
     );
@@ -459,102 +538,348 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 
 
 ## API
-### TSlidePopupRoute
+### TPopup
 #### 简介
-从屏幕的某个方向滑动弹出的Dialog框的路由,比如从顶部、底部、左、右滑出页面
-#### 默认构造方法
+弹出层入口:五向滑入 / 居中弹出,支持蒙层、bottom 操作区、center 面板外下方关闭区。
+
+ 通过 [show] 命令式打开;返回 [TPopupHandle] 用于关闭与再次打开。
+ 多次调用 [show] 会继续压入新的浮层路由,可用于叠加展示。
+
+ **示例**
+
+ ```dart
+ final handle = TPopup.show(
+   context,
+   options: TPopupOptions.bottom(
+     titleWidget: const Text('标题'),
+     child: MyPanel(),
+   ),
+ );
+ handle.close();
+ handle.open();
+ ```
+
+ 配置项见 [TPopupOptions];方向见 [TPopupPlacement]。
+
+#### 工厂构造方法
+
+##### TPopup._
+
+#### 静态方法
+
+##### TPopup.show
+
+打开浮层并压入独立 [PopupRoute]。
+
+ [context] 用于查找 [Navigator] 并展示浮层。
+
+ [options] 浮层配置;方向固定时推荐 [TPopupOptions.bottom] 等命名工厂。
+
+ 返回 [TPopupHandle],可用 [TPopupHandle.close]、[TPopupHandle.open]、
+ [TPopupHandle.isShowing] 控制与查询。
+ 重复调用会继续 push 新的浮层;若需互斥请在业务层管理。
+
+ [navigatorContext] 可选,指定承载浮层的 [Navigator] 的 context,默认 [context]。
+
+ [useRootNavigator] 为 true 时使用根 [Navigator](嵌套导航场景)。
+
+返回类型:`TPopupHandle`
 
 | 参数 | 类型 | 默认值 | 说明 |
 | --- | --- | --- | --- |
-| barrierClick | VoidCallback? | - | 蒙层点击事件,仅在[modalBarrierFull]为false时触发 |
-| barrierLabel |  | - |  |
-| builder | WidgetBuilder | - | 控件构建器 |
-| close | VoidCallback? | - | 关闭前事件 |
-| focusMove | bool | false | 是否有输入框获取焦点时整体平移避免输入框被遮挡 |
-| isDismissible | bool | true | 点击蒙层能否关闭 |
-| modalBarrierColor | Color? | Colors.black54 | 蒙层颜色 |
-| modalBarrierFull | bool | false | 是否全屏显示蒙层 |
-| modalHeight | double? | - | 弹出框高度 |
-| modalLeft | double? | 0 | 弹出框左侧距离 |
-| modalTop | double? | 0 | 弹出框顶部距离 |
-| modalWidth | double? | - | 弹出框宽度 |
-| open | VoidCallback? | - | 打开前事件 |
-| opened | VoidCallback? | - | 打开后事件 |
-| slideTransitionFrom | SlideTransitionFrom | SlideTransitionFrom.bottom | 设置从屏幕的哪个方向滑出 |
+| context | BuildContext | - | - |
+| options | TPopupOptions | - | 创建时传入的配置;每次 [open] 会按 [TPopupOptions.placement] 裁剪无效字段后使用。 |
+| navigatorContext | BuildContext? | - | 与 [TPopup.show] 的 [navigatorContext] 相同。 |
+| useRootNavigator | bool | false | 与 [TPopup.show] 的 [useRootNavigator] 相同。 |
 
-```
-```
 
-### TPopupBottomDisplayPanel
+### TPopupOptions
 #### 简介
-右上角带关闭的底部浮层面板
+[TPopup.show] 的配置对象。
+
+ ## 如何创建
+
+ | 场景 | 推荐用法 |
+ |------|----------|
+ | 弹出方向已知 | [TPopupOptions.bottom]、[TPopupOptions.center]、[TPopupOptions.top]、[TPopupOptions.left]、[TPopupOptions.right] |
+ | 方向由变量决定 | 默认构造并设置 [placement];传错字段会在 [TPopup.show] / [TPopupHandle.open] 时抛 [FlutterError] |
+
+ 命名工厂只暴露当前方向生效的字段(例如 [TPopupOptions.bottom] 无 [width] 参数)。
+
+ ## 字段与 [TPopupPlacement]
+
+ | [TPopupPlacement] | 头部 / 关闭区 | 尺寸 |
+ |-------------------|-------------|------|
+ | [TPopupPlacement.bottom] | [headerBuilder]、[titleWidget]、[cancelBuilder]、[confirmBuilder] | [height]、[inset] |
+ | [TPopupPlacement.center] | [closeBuilder] | [width]、[height] |
+ | [TPopupPlacement.top] | — | [height]、[inset] |
+ | [TPopupPlacement.left]、[TPopupPlacement.right] | — | [width]、[inset] |
+
+ ## Builder 三态([headerBuilder]、[cancelBuilder]、[confirmBuilder]、[closeBuilder])
+
+ | 传参方式 | 效果 |
+ |----------|------|
+ | 省略(使用默认值) | 渲染内置 UI |
+ | 显式 `null` | 隐藏该区域 |
+ | 自定义 [TPopupHeaderBuilder] / [TPopupSlotBuilder] | 完全替换;需自行提供交互与语义,可调用 `close` 关闭浮层 |
+
+ [titleWidget] 默认为 `null`,表示无标题内容。
+
+ 生命周期回调见 [onOpen]、[onOpened]、[onClose]、[onClosed]、[onVisibleChange]、[onOverlayClick]。
 #### 默认构造方法
 
 | 参数 | 类型 | 默认值 | 说明 |
 | --- | --- | --- | --- |
-| backgroundColor |  | - |  |
-| child |  | - |  |
-| closeClick | PopupClick? | - | 关闭按钮点击回调 |
-| closeColor | Color? | - | 关闭按钮颜色 |
-| closeSize | double? | - | 关闭按钮图标尺寸 |
-| draggable |  | - |  |
-| hideClose | bool | false | 是否隐藏关闭按钮 |
-| key |  | - |  |
-| maxHeightRatio |  | - |  |
-| minHeightRatio |  | - |  |
-| radius |  | - |  |
-| title |  | - |  |
-| titleColor |  | - |  |
-| titleFontSize | double? | - | 标题字体大小 |
-| titleLeft | bool | false | 标题是否靠左 |
+| animationDuration | Duration | const Duration(milliseconds: 240) | 打开/关闭动画时长。 |
+| backgroundColor | Color? | - | 内容区背景色,默认主题容器色。 |
+| cancelBuilder | TPopupSlotBuilder? | _kPopupDefaultCancel | bottom 左侧操作槽;仅 [headerBuilder] 为内置默认时生效。 内置默认为「取消」,点击触发 [TPopupTrigger.cancel]。 |
+| child | Widget | - | 浮层主体内容(必填)。 |
+| closeBuilder | TPopupSlotBuilder? | _kPopupDefaultClose | center 面板外下方关闭区;仅 [TPopupPlacement.center] 生效。三态见类文档「Builder 三态」。 内置默认点击触发 [TPopupTrigger.close]。 |
+| closeOnOverlayClick | bool? | - | - |
+| confirmBuilder | TPopupSlotBuilder? | _kPopupDefaultConfirm | bottom 右侧操作槽;仅 [headerBuilder] 为内置默认时生效。 内置默认为「确定」,点击触发 [TPopupTrigger.confirm]。 |
+| destroyOnClose | bool | false | 为 true 时路由 `maintainState` 为 false,关闭后不保留路由内 State。 |
+| headerBuilder | TPopupHeaderBuilder? | _kPopupDefaultHeader | bottom 头部;仅 [TPopupPlacement.bottom] 生效。三态见类文档「Builder 三态」。 自定义时忽略 [titleWidget]、[cancelBuilder]、[confirmBuilder]。 |
+| height | double? | - | 高度;[TPopupPlacement.top]、[TPopupPlacement.bottom] 生效;[TPopupPlacement.center] 约束面板尺寸。 |
+| inset | TPopupInset? | - | 交叉轴边缘留白;具体类型由 [placement] 决定。 * [TPopupPlacement.bottom] 使用 [TPopupBottomInset] * [TPopupPlacement.top] 使用 [TPopupTopInset] * [TPopupPlacement.left] 使用 [TPopupLeftInset] * [TPopupPlacement.right] 使用 [TPopupRightInset] * [TPopupPlacement.center] 不支持 |
+| modal | bool | true | 是否以模态方式展示;为 true 时阻断背景交互与底层语义/焦点。 结合 [showOverlay] 可表达三种模式: * `modal=true, showOverlay=true`:标准模态弹层 * `modal=true, showOverlay=false`:透明模态弹层 * `modal=false, showOverlay=false`:非模态浮层 |
+| onClose | VoidCallback? | - | 开始关闭(与 [onVisibleChange] 的 `visible: false` 同期)。 |
+| onClosed | VoidCallback? | - | 当前展示周期真正结束。 大多数场景下会在关闭动画结束后触发;非栈顶路由被直接移除时不保证存在关闭动画。 |
+| onOpen | VoidCallback? | - | 路由 push 时(打开动画开始前)。 |
+| onOpened | VoidCallback? | - | 打开动画结束。 |
+| onOverlayClick | VoidCallback? | - | 蒙层点击;是否关闭取决于 [closeOnOverlayClick]。 |
+| onVisibleChange | TPopupVisibleChangeCallback? | - | 显隐变化;第二个参数为 [TPopupTrigger]。 |
+| overlayColor | Color? | - | 蒙层颜色,默认 black54。 |
+| overlayOpacity | double? | - | 蒙层透明度系数(0–1),与 [overlayColor] 的 alpha 相乘后用于绘制。 |
+| placement | TPopupPlacement | TPopupPlacement.bottom | 出现位置,默认 [TPopupPlacement.bottom]。 |
+| radius | double? | - | 内容区圆角,默认主题大圆角。 |
+| showOverlay | bool | true | 是否绘制半透明蒙层。 当 [modal] 为 true 且此值为 false 时,为“透明模态弹层”。 |
+| titleWidget | Widget? | - | bottom 标题插槽;仅 [headerBuilder] 为内置默认时生效。`null` 表示无标题。 |
+| width | double? | - | 宽度;[TPopupPlacement.left]、[TPopupPlacement.right]、[TPopupPlacement.center] 生效。 |
+
+
+#### 工厂构造方法
+
+##### TPopupOptions.bottom
+
+创建 [TPopupPlacement.bottom] 配置。
+
+ 固定 [placement] 为 [TPopupPlacement.bottom];默认带内置头部。
+ 蒙层、动画、生命周期等字段语义见同名成员文档。
 
-```
-```
+| 参数 | 类型 | 默认值 | 说明 |
+| --- | --- | --- | --- |
+| child | Widget | - | 浮层主体内容(必填)。 |
+| height | double? | - | 高度;[TPopupPlacement.top]、[TPopupPlacement.bottom] 生效;[TPopupPlacement.center] 约束面板尺寸。 |
+| inset | TPopupBottomInset? | - | 交叉轴边缘留白;具体类型由 [placement] 决定。 * [TPopupPlacement.bottom] 使用 [TPopupBottomInset] * [TPopupPlacement.top] 使用 [TPopupTopInset] * [TPopupPlacement.left] 使用 [TPopupLeftInset] * [TPopupPlacement.right] 使用 [TPopupRightInset] * [TPopupPlacement.center] 不支持 |
+| headerBuilder | TPopupHeaderBuilder? | _kPopupDefaultHeader | bottom 头部;仅 [TPopupPlacement.bottom] 生效。三态见类文档「Builder 三态」。 自定义时忽略 [titleWidget]、[cancelBuilder]、[confirmBuilder]。 |
+| titleWidget | Widget? | - | bottom 标题插槽;仅 [headerBuilder] 为内置默认时生效。`null` 表示无标题。 |
+| cancelBuilder | TPopupSlotBuilder? | _kPopupDefaultCancel | bottom 左侧操作槽;仅 [headerBuilder] 为内置默认时生效。 内置默认为「取消」,点击触发 [TPopupTrigger.cancel]。 |
+| confirmBuilder | TPopupSlotBuilder? | _kPopupDefaultConfirm | bottom 右侧操作槽;仅 [headerBuilder] 为内置默认时生效。 内置默认为「确定」,点击触发 [TPopupTrigger.confirm]。 |
+| radius | double? | - | 内容区圆角,默认主题大圆角。 |
+| backgroundColor | Color? | - | 内容区背景色,默认主题容器色。 |
+| showOverlay | bool | true | 是否绘制半透明蒙层。 当 [modal] 为 true 且此值为 false 时,为“透明模态弹层”。 |
+| closeOnOverlayClick | bool? | - | - |
+| overlayColor | Color? | - | 蒙层颜色,默认 black54。 |
+| overlayOpacity | double? | - | 蒙层透明度系数(0–1),与 [overlayColor] 的 alpha 相乘后用于绘制。 |
+| modal | bool | true | 是否以模态方式展示;为 true 时阻断背景交互与底层语义/焦点。 结合 [showOverlay] 可表达三种模式: * `modal=true, showOverlay=true`:标准模态弹层 * `modal=true, showOverlay=false`:透明模态弹层 * `modal=false, showOverlay=false`:非模态浮层 |
+| destroyOnClose | bool | false | 为 true 时路由 `maintainState` 为 false,关闭后不保留路由内 State。 |
+| animationDuration | Duration | const Duration(milliseconds: 240) | 打开/关闭动画时长。 |
+| onOpen | VoidCallback? | - | 路由 push 时(打开动画开始前)。 |
+| onOpened | VoidCallback? | - | 打开动画结束。 |
+| onClose | VoidCallback? | - | 开始关闭(与 [onVisibleChange] 的 `visible: false` 同期)。 |
+| onClosed | VoidCallback? | - | 当前展示周期真正结束。 大多数场景下会在关闭动画结束后触发;非栈顶路由被直接移除时不保证存在关闭动画。 |
+| onVisibleChange | TPopupVisibleChangeCallback? | - | 显隐变化;第二个参数为 [TPopupTrigger]。 |
+| onOverlayClick | VoidCallback? | - | 蒙层点击;是否关闭取决于 [closeOnOverlayClick]。 |
+
+
+##### TPopupOptions.center
+
+创建 [TPopupPlacement.center] 配置。
+
+ 固定 [placement] 为 [TPopupPlacement.center];默认展示面板外下方圆形关闭按钮。
 
-### TPopupBottomConfirmPanel
-#### 简介
-带确认的底部浮层面板
-#### 默认构造方法
+| 参数 | 类型 | 默认值 | 说明 |
+| --- | --- | --- | --- |
+| child | Widget | - | 浮层主体内容(必填)。 |
+| width | double? | - | 宽度;[TPopupPlacement.left]、[TPopupPlacement.right]、[TPopupPlacement.center] 生效。 |
+| height | double? | - | 高度;[TPopupPlacement.top]、[TPopupPlacement.bottom] 生效;[TPopupPlacement.center] 约束面板尺寸。 |
+| closeBuilder | TPopupSlotBuilder? | _kPopupDefaultClose | center 面板外下方关闭区;仅 [TPopupPlacement.center] 生效。三态见类文档「Builder 三态」。 内置默认点击触发 [TPopupTrigger.close]。 |
+| radius | double? | - | 内容区圆角,默认主题大圆角。 |
+| backgroundColor | Color? | - | 内容区背景色,默认主题容器色。 |
+| showOverlay | bool | true | 是否绘制半透明蒙层。 当 [modal] 为 true 且此值为 false 时,为“透明模态弹层”。 |
+| closeOnOverlayClick | bool? | - | - |
+| overlayColor | Color? | - | 蒙层颜色,默认 black54。 |
+| overlayOpacity | double? | - | 蒙层透明度系数(0–1),与 [overlayColor] 的 alpha 相乘后用于绘制。 |
+| modal | bool | true | 是否以模态方式展示;为 true 时阻断背景交互与底层语义/焦点。 结合 [showOverlay] 可表达三种模式: * `modal=true, showOverlay=true`:标准模态弹层 * `modal=true, showOverlay=false`:透明模态弹层 * `modal=false, showOverlay=false`:非模态浮层 |
+| destroyOnClose | bool | false | 为 true 时路由 `maintainState` 为 false,关闭后不保留路由内 State。 |
+| animationDuration | Duration | const Duration(milliseconds: 240) | 打开/关闭动画时长。 |
+| onOpen | VoidCallback? | - | 路由 push 时(打开动画开始前)。 |
+| onOpened | VoidCallback? | - | 打开动画结束。 |
+| onClose | VoidCallback? | - | 开始关闭(与 [onVisibleChange] 的 `visible: false` 同期)。 |
+| onClosed | VoidCallback? | - | 当前展示周期真正结束。 大多数场景下会在关闭动画结束后触发;非栈顶路由被直接移除时不保证存在关闭动画。 |
+| onVisibleChange | TPopupVisibleChangeCallback? | - | 显隐变化;第二个参数为 [TPopupTrigger]。 |
+| onOverlayClick | VoidCallback? | - | 蒙层点击;是否关闭取决于 [closeOnOverlayClick]。 |
+
+
+##### TPopupOptions.left
+
+创建 [TPopupPlacement.left] 配置。
+
+ 固定 [placement] 为 [TPopupPlacement.left];未传 [width] 时布局默认宽度 280。
 
 | 参数 | 类型 | 默认值 | 说明 |
 | --- | --- | --- | --- |
-| backgroundColor |  | - |  |
-| child |  | - |  |
-| draggable |  | - |  |
-| key |  | - |  |
-| leftClick | PopupClick? | - | 左边文本点击回调 |
-| leftText | String? | - | 左边文本 |
-| leftTextColor | Color? | - | 左边文本颜色 |
-| leftTextFontSize | double? | - | 左边文本字体大小 |
-| maxHeightRatio |  | - |  |
-| minHeightRatio |  | - |  |
-| radius |  | - |  |
-| rightClick | PopupClick? | - | 右边文本点击回调 |
-| rightText | String? | - | 右边文本 |
-| rightTextColor | Color? | - | 右边文本颜色 |
-| rightTextFontSize | double? | - | 右边文本字体大小 |
-| title |  | - |  |
-| titleColor |  | - |  |
-| titleFontSize | double? | - | 标题字体大小 |
+| child | Widget | - | 浮层主体内容(必填)。 |
+| width | double? | - | 宽度;[TPopupPlacement.left]、[TPopupPlacement.right]、[TPopupPlacement.center] 生效。 |
+| inset | TPopupLeftInset? | - | 交叉轴边缘留白;具体类型由 [placement] 决定。 * [TPopupPlacement.bottom] 使用 [TPopupBottomInset] * [TPopupPlacement.top] 使用 [TPopupTopInset] * [TPopupPlacement.left] 使用 [TPopupLeftInset] * [TPopupPlacement.right] 使用 [TPopupRightInset] * [TPopupPlacement.center] 不支持 |
+| radius | double? | - | 内容区圆角,默认主题大圆角。 |
+| backgroundColor | Color? | - | 内容区背景色,默认主题容器色。 |
+| showOverlay | bool | true | 是否绘制半透明蒙层。 当 [modal] 为 true 且此值为 false 时,为“透明模态弹层”。 |
+| closeOnOverlayClick | bool? | - | - |
+| overlayColor | Color? | - | 蒙层颜色,默认 black54。 |
+| overlayOpacity | double? | - | 蒙层透明度系数(0–1),与 [overlayColor] 的 alpha 相乘后用于绘制。 |
+| modal | bool | true | 是否以模态方式展示;为 true 时阻断背景交互与底层语义/焦点。 结合 [showOverlay] 可表达三种模式: * `modal=true, showOverlay=true`:标准模态弹层 * `modal=true, showOverlay=false`:透明模态弹层 * `modal=false, showOverlay=false`:非模态浮层 |
+| destroyOnClose | bool | false | 为 true 时路由 `maintainState` 为 false,关闭后不保留路由内 State。 |
+| animationDuration | Duration | const Duration(milliseconds: 240) | 打开/关闭动画时长。 |
+| onOpen | VoidCallback? | - | 路由 push 时(打开动画开始前)。 |
+| onOpened | VoidCallback? | - | 打开动画结束。 |
+| onClose | VoidCallback? | - | 开始关闭(与 [onVisibleChange] 的 `visible: false` 同期)。 |
+| onClosed | VoidCallback? | - | 当前展示周期真正结束。 大多数场景下会在关闭动画结束后触发;非栈顶路由被直接移除时不保证存在关闭动画。 |
+| onVisibleChange | TPopupVisibleChangeCallback? | - | 显隐变化;第二个参数为 [TPopupTrigger]。 |
+| onOverlayClick | VoidCallback? | - | 蒙层点击;是否关闭取决于 [closeOnOverlayClick]。 |
+
+
+##### TPopupOptions.right
+
+创建 [TPopupPlacement.right] 配置。
+
+ 固定 [placement] 为 [TPopupPlacement.right];未传 [width] 时布局默认宽度 280。
 
-```
-```
+| 参数 | 类型 | 默认值 | 说明 |
+| --- | --- | --- | --- |
+| child | Widget | - | 浮层主体内容(必填)。 |
+| width | double? | - | 宽度;[TPopupPlacement.left]、[TPopupPlacement.right]、[TPopupPlacement.center] 生效。 |
+| inset | TPopupRightInset? | - | 交叉轴边缘留白;具体类型由 [placement] 决定。 * [TPopupPlacement.bottom] 使用 [TPopupBottomInset] * [TPopupPlacement.top] 使用 [TPopupTopInset] * [TPopupPlacement.left] 使用 [TPopupLeftInset] * [TPopupPlacement.right] 使用 [TPopupRightInset] * [TPopupPlacement.center] 不支持 |
+| radius | double? | - | 内容区圆角,默认主题大圆角。 |
+| backgroundColor | Color? | - | 内容区背景色,默认主题容器色。 |
+| showOverlay | bool | true | 是否绘制半透明蒙层。 当 [modal] 为 true 且此值为 false 时,为“透明模态弹层”。 |
+| closeOnOverlayClick | bool? | - | - |
+| overlayColor | Color? | - | 蒙层颜色,默认 black54。 |
+| overlayOpacity | double? | - | 蒙层透明度系数(0–1),与 [overlayColor] 的 alpha 相乘后用于绘制。 |
+| modal | bool | true | 是否以模态方式展示;为 true 时阻断背景交互与底层语义/焦点。 结合 [showOverlay] 可表达三种模式: * `modal=true, showOverlay=true`:标准模态弹层 * `modal=true, showOverlay=false`:透明模态弹层 * `modal=false, showOverlay=false`:非模态浮层 |
+| destroyOnClose | bool | false | 为 true 时路由 `maintainState` 为 false,关闭后不保留路由内 State。 |
+| animationDuration | Duration | const Duration(milliseconds: 240) | 打开/关闭动画时长。 |
+| onOpen | VoidCallback? | - | 路由 push 时(打开动画开始前)。 |
+| onOpened | VoidCallback? | - | 打开动画结束。 |
+| onClose | VoidCallback? | - | 开始关闭(与 [onVisibleChange] 的 `visible: false` 同期)。 |
+| onClosed | VoidCallback? | - | 当前展示周期真正结束。 大多数场景下会在关闭动画结束后触发;非栈顶路由被直接移除时不保证存在关闭动画。 |
+| onVisibleChange | TPopupVisibleChangeCallback? | - | 显隐变化;第二个参数为 [TPopupTrigger]。 |
+| onOverlayClick | VoidCallback? | - | 蒙层点击;是否关闭取决于 [closeOnOverlayClick]。 |
+
+
+##### TPopupOptions.top
+
+创建 [TPopupPlacement.top] 配置。
+
+ 固定 [placement] 为 [TPopupPlacement.top];无内置头部。
 
-### TPopupCenterPanel
+| 参数 | 类型 | 默认值 | 说明 |
+| --- | --- | --- | --- |
+| child | Widget | - | 浮层主体内容(必填)。 |
+| height | double? | - | 高度;[TPopupPlacement.top]、[TPopupPlacement.bottom] 生效;[TPopupPlacement.center] 约束面板尺寸。 |
+| inset | TPopupTopInset? | - | 交叉轴边缘留白;具体类型由 [placement] 决定。 * [TPopupPlacement.bottom] 使用 [TPopupBottomInset] * [TPopupPlacement.top] 使用 [TPopupTopInset] * [TPopupPlacement.left] 使用 [TPopupLeftInset] * [TPopupPlacement.right] 使用 [TPopupRightInset] * [TPopupPlacement.center] 不支持 |
+| radius | double? | - | 内容区圆角,默认主题大圆角。 |
+| backgroundColor | Color? | - | 内容区背景色,默认主题容器色。 |
+| showOverlay | bool | true | 是否绘制半透明蒙层。 当 [modal] 为 true 且此值为 false 时,为“透明模态弹层”。 |
+| closeOnOverlayClick | bool? | - | - |
+| overlayColor | Color? | - | 蒙层颜色,默认 black54。 |
+| overlayOpacity | double? | - | 蒙层透明度系数(0–1),与 [overlayColor] 的 alpha 相乘后用于绘制。 |
+| modal | bool | true | 是否以模态方式展示;为 true 时阻断背景交互与底层语义/焦点。 结合 [showOverlay] 可表达三种模式: * `modal=true, showOverlay=true`:标准模态弹层 * `modal=true, showOverlay=false`:透明模态弹层 * `modal=false, showOverlay=false`:非模态浮层 |
+| destroyOnClose | bool | false | 为 true 时路由 `maintainState` 为 false,关闭后不保留路由内 State。 |
+| animationDuration | Duration | const Duration(milliseconds: 240) | 打开/关闭动画时长。 |
+| onOpen | VoidCallback? | - | 路由 push 时(打开动画开始前)。 |
+| onOpened | VoidCallback? | - | 打开动画结束。 |
+| onClose | VoidCallback? | - | 开始关闭(与 [onVisibleChange] 的 `visible: false` 同期)。 |
+| onClosed | VoidCallback? | - | 当前展示周期真正结束。 大多数场景下会在关闭动画结束后触发;非栈顶路由被直接移除时不保证存在关闭动画。 |
+| onVisibleChange | TPopupVisibleChangeCallback? | - | 显隐变化;第二个参数为 [TPopupTrigger]。 |
+| onOverlayClick | VoidCallback? | - | 蒙层点击;是否关闭取决于 [closeOnOverlayClick]。 |
+
+
+### TPopupHandle
 #### 简介
-居中浮层面板
-#### 默认构造方法
+[TPopup.show] 的返回值,用于控制同一份 [TPopupOptions] 的多次打开与关闭。
+
+ **示例**
+
+ ```dart
+ final handle = TPopup.show(
+   context,
+   options: TPopupOptions.bottom(child: panel),
+ );
+ handle.close();
+ handle.open(); // 可省略 context,复用已缓存的 Navigator
+ ```
+#### 公开属性
+
+| 属性 | 类型 | 默认值 | 说明 |
+| --- | --- | --- | --- |
+| navigatorContext | BuildContext? | - | 与 [TPopup.show] 的 [navigatorContext] 相同。 |
+| options | TPopupOptions | - | 创建时传入的配置;每次 [open] 会按 [TPopupOptions.placement] 裁剪无效字段后使用。 |
+| useRootNavigator | bool | - | 与 [TPopup.show] 的 [useRootNavigator] 相同。 |
+
+
+#### 工厂构造方法
+
+##### TPopupHandle._
 
 | 参数 | 类型 | 默认值 | 说明 |
 | --- | --- | --- | --- |
-| backgroundColor | Color? | - | 背景颜色 |
-| child | Widget | - | 子控件 |
-| closeClick | PopupClick? | - | 关闭按钮点击回调 |
-| closeColor | Color? | - | 关闭按钮颜色 |
-| closeSize | double? | - | 关闭按钮图标尺寸 |
-| closeUnderBottom | bool | false | 关闭按钮是否在视图框下方 |
-| key |  | - |  |
-| radius | double? | - | 圆角 |
+| options | TPopupOptions | - | 创建时传入的配置;每次 [open] 会按 [TPopupOptions.placement] 裁剪无效字段后使用。 |
+| navigatorContext | BuildContext? | - | 与 [TPopup.show] 的 [navigatorContext] 相同。 |
+| useRootNavigator | bool | false | 与 [TPopup.show] 的 [useRootNavigator] 相同。 |
+
+
+### TPopupPlacement
+#### 简介
+浮层出现方向;决定 [TPopupOptions] 中哪些字段生效。
+
+ 与 [TPopupOptions] 类文档中的「字段与 placement」表对应。
+ 方向固定时请用 [TPopupOptions.bottom]、[TPopupOptions.center] 等命名工厂。
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| top | 自顶部滑入;使用 [TPopupOptions.height]、[TPopupOptions.inset]([TPopupTopInset])。 |
+| left | 自左侧滑入;使用 [TPopupOptions.width]、[TPopupOptions.inset]([TPopupLeftInset])。 |
+| right | 自右侧滑入;使用 [TPopupOptions.width]、[TPopupOptions.inset]([TPopupRightInset])。 |
+| bottom | 自底部滑入;默认内置头部;使用 [TPopupOptions.height]、[TPopupOptions.inset]([TPopupBottomInset])。 |
+| center | 屏幕居中;使用 [TPopupOptions.closeBuilder] 控制面板外下方关闭区。 |
+
+
+### TPopupTrigger
+#### 简介
+浮层关闭或显隐变化时的触发来源。
+
+ 作为 [TPopupVisibleChangeCallback] 的第二个参数,以及关闭流程中的语义标记。
+
+ 内置控件会映射为 [TPopupTrigger.overlay]、[TPopupTrigger.cancel]、
+ [TPopupTrigger.confirm]、[TPopupTrigger.close];
+ [TPopupHandle.close] 为 [TPopupTrigger.api];系统返回为
+ [TPopupTrigger.systemBack];[headerBuilder] 内调用 `close` 等为
+ [TPopupTrigger.custom]。
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| overlay | 点击蒙层,且 [TPopupOptions.closeOnOverlayClick] 为 true。 |
+| cancel | 点击 bottom 取消语义槽位(含默认与自定义 [TPopupOptions.cancelBuilder])。 |
+| confirm | 点击 bottom 确认语义槽位(含默认与自定义 [TPopupOptions.confirmBuilder])。 |
+| close | 点击 center 关闭语义槽位(含默认与自定义 [TPopupOptions.closeBuilder])。 |
+| api | 外部 API 主动触发的显隐变化,如 [TPopupHandle.close] 或打开事件。 |
+| systemBack | 系统返回键或系统路由返回触发的关闭。 |
+| custom | 无框架预设动作语义的自定义关闭,如 [headerBuilder] 内调用 `close`。 |
 
 
   
\ No newline at end of file
diff --git a/tdesign-site/src/progress/README.md b/tdesign-site/src/progress/README.md
index 67c7a2a62..5c04f47bb 100644
--- a/tdesign-site/src/progress/README.md
+++ b/tdesign-site/src/progress/README.md
@@ -323,12 +323,12 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 
 | 参数 | 类型 | 默认值 | 说明 |
 | --- | --- | --- | --- |
-| animationDuration | int? | 300 | 动画持续时间(正整数,单位为毫秒) |
+| animationDuration | int | 300 | 动画持续时间(正整数,单位为毫秒) |
 | backgroundColor | Color? | - | 进度条背景颜色 |
 | circleRadius | double? | - | 环形进度条半径(正数) |
 | color | Color? | - | 进度条颜色 |
 | customProgressLabel | Widget? | - | 自定义标签 |
-| key |  | - |  |
+| key | Key? | - | 组件标识,用于区分或保留组件状态。 |
 | label | TLabelWidget? | - | 进度条标签 |
 | labelWidgetAlignment | Alignment? | - | 自定义标签对齐方式 |
 | labelWidgetWidth | double? | - | 自定义标签宽度 |
@@ -343,4 +343,39 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 | value | double? | - | 进度值(0.0 到 1.0 之间的正数) |
 
 
+### TProgressType
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| linear | - |
+| circular | - |
+| micro | - |
+| button | - |
+
+
+### TProgressLabelPosition
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| inside | - |
+| left | - |
+| right | - |
+
+
+### TProgressStatus
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| primary | - |
+| warning | - |
+| danger | - |
+| success | - |
+
+
   
\ No newline at end of file
diff --git a/tdesign-site/src/pull-down-refresh/README.md b/tdesign-site/src/pull-down-refresh/README.md
index d063b28cf..8bc3e0705 100644
--- a/tdesign-site/src/pull-down-refresh/README.md
+++ b/tdesign-site/src/pull-down-refresh/README.md
@@ -90,41 +90,41 @@ TDesign刷新头部
 | 参数 | 类型 | 默认值 | 说明 |
 | --- | --- | --- | --- |
 | backgroundColor | Color? | - | 背景颜色 |
-| clamping |  | - |  |
+| clamping | bool? | - | - |
 | completeDuration | Duration? | - | 完成延时 |
 | enableHapticFeedback | bool | true | 开启震动反馈 |
 | enableInfiniteRefresh | bool | false | 是否开启无限刷新 |
 | extent | double? | 48.0 | Header容器高度 |
 | float | bool | false | 是否悬浮 |
-| frictionFactor |  | - |  |
-| hapticFeedback |  | - |  |
-| hitOver |  | - |  |
-| horizontalFrictionFactor |  | - |  |
-| horizontalReadySpringBuilder |  | - |  |
-| horizontalSpring |  | - |  |
-| infiniteHitOver |  | - |  |
+| frictionFactor | - | - | - |
+| hapticFeedback | bool? | - | - |
+| hitOver | - | - | - |
+| horizontalFrictionFactor | - | - | - |
+| horizontalReadySpringBuilder | - | - | - |
+| horizontalSpring | - | - | - |
+| infiniteHitOver | bool? | - | - |
 | infiniteOffset | double? | - | 无限刷新偏移量 |
 | key | Key? | - | Key |
-| listenable |  | - |  |
+| listenable | - | - | - |
 | loadingIcon | TLoadingIcon | TLoadingIcon.circle | loading样式 |
-| maxOverOffset |  | - |  |
-| notifyWhenInvisible |  | - |  |
+| maxOverOffset | - | - | - |
+| notifyWhenInvisible | - | - | - |
 | overScroll | bool | true | 越界滚动([enableInfiniteRefresh]为true或[infiniteOffset]有值时生效) |
-| position |  | - |  |
-| processedDuration |  | - |  |
-| readySpringBuilder |  | - |  |
-| safeArea |  | false |  |
-| secondaryCloseTriggerOffset |  | - |  |
-| secondaryDimension |  | - |  |
-| secondaryTriggerOffset |  | - |  |
-| secondaryVelocity |  | - |  |
-| spring |  | - |  |
-| springRebound |  | - |  |
+| position | - | - | - |
+| processedDuration | Duration? | - | - |
+| readySpringBuilder | - | - | - |
+| safeArea | - | false | - |
+| secondaryCloseTriggerOffset | - | - | - |
+| secondaryDimension | - | - | - |
+| secondaryTriggerOffset | - | - | - |
+| secondaryVelocity | - | - | - |
+| spring | - | - | - |
+| springRebound | - | - | - |
 | triggerDistance | double | 48.0 | 触发刷新任务的偏移量,同[triggerOffset] |
-| triggerOffset |  | - |  |
-| triggerWhenReach |  | - |  |
-| triggerWhenRelease |  | - |  |
-| triggerWhenReleaseNoWait |  | - |  |
+| triggerOffset | double? | - | - |
+| triggerWhenReach | - | - | - |
+| triggerWhenRelease | - | - | - |
+| triggerWhenReleaseNoWait | - | - | - |
 
 
   
\ No newline at end of file
diff --git a/tdesign-site/src/radio/README.md b/tdesign-site/src/radio/README.md
index 7464c515b..4c9136787 100644
--- a/tdesign-site/src/radio/README.md
+++ b/tdesign-site/src/radio/README.md
@@ -338,34 +338,32 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 
 | 参数 | 类型 | 默认值 | 说明 |
 | --- | --- | --- | --- |
-| backgroundColor |  | - |  |
-| cardMode |  | - |  |
-| checkBoxLeftSpace |  | - |  |
-| contentDirection |  | TContentDirection.right |  |
-| customContentBuilder |  | - |  |
-| customIconBuilder |  | - |  |
-| customSpace |  | - |  |
-| disableColor |  | - |  |
-| enable |  | true |  |
-| id |  | - |  |
-| insetSpacing |  | - |  |
-| key |  | - |  |
+| backgroundColor | Color? | - | - |
+| cardMode | bool? | - | - |
+| checkBoxLeftSpace | double? | - | - |
+| contentDirection | TContentDirection | TContentDirection.right | - |
+| customContentBuilder | ContentBuilder? | - | - |
+| customIconBuilder | IconBuilder? | - | - |
+| customSpace | EdgeInsetsGeometry? | - | - |
+| disableColor | Color? | - | - |
+| enable | bool | true | - |
+| id | String? | - | - |
+| insetSpacing | double? | - | - |
+| key | Key? | - | 组件标识,用于区分或保留组件状态。 |
 | radioStyle | TRadioStyle | TRadioStyle.circle | 单选框按钮样式 |
-| selectColor |  | - |  |
-| showDivider | bool | - | 是否显示下划线 |
-| size |  | TCheckBoxSize.small |  |
-| spacing |  | - |  |
-| subTitle |  | - |  |
-| subTitleColor |  | - |  |
-| subTitleFont |  | - |  |
-| subTitleMaxLine |  | 1 |  |
-| title |  | - |  |
-| titleColor |  | - |  |
-| titleFont |  | - |  |
-| titleMaxLine |  | 1 |  |
+| selectColor | Color? | - | - |
+| showDivider | bool? | - | - |
+| size | TCheckBoxSize | TCheckBoxSize.small | - |
+| spacing | double? | - | - |
+| subTitle | String? | - | - |
+| subTitleColor | Color? | - | - |
+| subTitleFont | Font? | - | - |
+| subTitleMaxLine | int | 1 | - |
+| title | String? | - | - |
+| titleColor | Color? | - | - |
+| titleFont | Font? | - | - |
+| titleMaxLine | int | 1 | - |
 
-```
-```
 
 ### TRadioGroup
 #### 简介
@@ -378,25 +376,45 @@ RadioGroup分组对象,继承自TCheckboxGroup,字段含义与父类一致
 
 | 参数 | 类型 | 默认值 | 说明 |
 | --- | --- | --- | --- |
-| cardMode |  | false |  |
-| child |  | - |  |
-| contentDirection |  | - |  |
-| controller |  | - |  |
-| customContentBuilder |  | - |  |
-| customIconBuilder |  | - |  |
-| direction |  | - |  |
-| directionalTdRadios |  | - |  |
+| cardMode | bool | false | - |
+| child | Widget? | - | - |
+| contentDirection | TContentDirection? | - | - |
+| controller | TCheckboxGroupController? | - | - |
+| customContentBuilder | ContentBuilder? | - | - |
+| customIconBuilder | IconBuilder? | - | - |
+| direction | Axis? | - | - |
+| directionalTdRadios | List? | - | - |
 | divider | Widget? | - | 自定义下划线 |
-| key |  | - |  |
-| onRadioGroupChange |  | - |  |
-| passThrough |  | - |  |
+| key | Key? | - | 组件标识,用于区分或保留组件状态。 |
+| onRadioGroupChange | OnRadioGroupChange? | - | - |
+| passThrough | bool? | - | - |
 | radioCheckStyle | TRadioStyle? | - | 勾选样式 |
 | rowCount | int | 1 | 每行几列 |
-| selectId |  | - |  |
+| selectId | String? | - | - |
 | showDivider | bool | false | 是否显示下划线 |
-| spacing |  | - |  |
+| spacing | double? | - | - |
 | strictMode | bool | true | 严格模式下,用户不能取消勾选,只能切换选择项, |
-| titleMaxLine |  | - |  |
+| titleMaxLine | int? | - | - |
+
+
+### TRadioStyle
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| circle | - |
+| square | - |
+| check | - |
+| hollowCircle | - |
+
+
+### OnRadioGroupChange
+#### 类型定义
+
+```dart
+typedef OnRadioGroupChange = void Function(String? selectedId);
+```
 
 
   
\ No newline at end of file
diff --git a/tdesign-site/src/rate/README.md b/tdesign-site/src/rate/README.md
index f5472215c..5d53b6ba7 100644
--- a/tdesign-site/src/rate/README.md
+++ b/tdesign-site/src/rate/README.md
@@ -217,7 +217,7 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 | 参数 | 类型 | 默认值 | 说明 |
 | --- | --- | --- | --- |
 | allowHalf | bool? | false | 是否允许半选 |
-| builderText | Widget Function(BuildContext context, double value)? | - | 评分等级对应的辅助文字自定义构建,优先级高于[texts] |
+| builderText | Widget Function(BuildContext context, double value)? | - | 评分等级对应的辅助文字自定义构建,优先级高于[texts] 配置后,会忽略[texts],[textWidth],[iconTextGap] |
 | color | List? | - | 评分图标的颜色,示例:[选中颜色] / [选中颜色,未选中颜色],默认:[TTheme.of(context).warningColor5, TTheme.of(context).grayColor4] |
 | count | int? | 5 | 评分的数量 |
 | crossAxisAlignment | CrossAxisAlignment? | CrossAxisAlignment.center | 评分图标与辅助文字的交叉轴对齐方式 |
@@ -226,16 +226,27 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 | gap | double? | - | 评分图标的间距,默认:TTheme.of(context).spacer8 |
 | icon | List? | - | 自定义评分图标,[选中和未选中图标] / [选中图标,未选中图标],默认:[TIcons.star_filled] |
 | iconTextGap | double? | - | 评分图标与辅助文字的间距,默认:[TTheme.of(context).spacer16] |
-| key |  | - |  |
+| key | Key? | - | 组件标识,用于区分或保留组件状态。 |
 | mainAxisAlignment | MainAxisAlignment? | MainAxisAlignment.start | 评分图标与辅助文字的主轴对齐方式 |
 | mainAxisSize | MainAxisSize? | MainAxisSize.min | 评分图标与辅助文字主轴方向上如何占用空间 |
 | onChange | void Function(double value)? | - | 评分数改变时触发 |
 | placement | PlacementEnum? | PlacementEnum.top | 选择评分弹框的位置,值为[PlacementEnum.none]表示不显示评分弹框。 |
 | showText | bool? | false | 是否显示对应的辅助文字 |
 | size | double? | 24.0 | 评分图标的大小 |
-| texts | List? | const ['极差', '失望', '一般', '满意', '惊喜'] | 评分等级对应的辅助文字, |
+| texts | List? | const ['极差', '失望', '一般', '满意', '惊喜'] | 评分等级对应的辅助文字, 当[allowHalf]为false时长度应与[count]一致, 当[allowHalf]为true时长度应为[count]的两倍, 自定义值示例:['1分', '2分', '3分', '4分', '5分']。 |
 | textWidth | double? | 48.0 | 评分等级对应的辅助文字宽度 |
 | value | double? | 0 | 选择评分的值 |
 
 
+### PlacementEnum
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| none | - |
+| top | - |
+| bottom | - |
+
+
   
\ No newline at end of file
diff --git a/tdesign-site/src/result/README.md b/tdesign-site/src/result/README.md
index 47fa4faf5..9763982e9 100644
--- a/tdesign-site/src/result/README.md
+++ b/tdesign-site/src/result/README.md
@@ -290,10 +290,22 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 | --- | --- | --- | --- |
 | description | String? | - | 描述文本,用于提供额外信息 |
 | icon | Widget? | - | 图标组件,用于在结果中显示一个图标 |
-| key |  | - |  |
+| key | Key? | - | 组件标识,用于区分或保留组件状态。 |
 | theme | TResultTheme | TResultTheme.defaultTheme | 主题样式,默认主题样式为defaultTheme |
 | title | String | '' | 标题文本,显示结果的主要信息,默认标题为空字符串 |
 | titleStyle | TextStyle? | - | 自定义字体样式,用于设置标题文本的样式 |
 
 
+### TResultTheme
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| defaultTheme | - |
+| success | - |
+| warning | - |
+| error | - |
+
+
   
\ No newline at end of file
diff --git a/tdesign-site/src/search/README.md b/tdesign-site/src/search/README.md
index 51146c4ee..93b973a62 100644
--- a/tdesign-site/src/search/README.md
+++ b/tdesign-site/src/search/README.md
@@ -130,7 +130,7 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 | enabled | bool? | - | 是否禁用 |
 | focusNode | FocusNode? | - | 自定义焦点 |
 | inputAction | TextInputAction? | - | 键盘动作类型 |
-| key |  | - |  |
+| key | Key? | - | 组件标识,用于区分或保留组件状态。 |
 | mediumStyle | bool | false | 是否在导航栏中的样式 |
 | needCancel | bool | false | 是否需要取消按钮 |
 | onActionClick | TSearchBarEvent? | - | 自定义操作回调 |
@@ -146,4 +146,52 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 | style | TSearchStyle? | TSearchStyle.square | 样式 |
 
 
+### TSearchStyle
+#### 简介
+搜索框的样式
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| square | 方形 |
+| round | 圆形 |
+
+
+### TSearchAlignment
+#### 简介
+搜索框对齐方式
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| left | 默认头部对齐 |
+| center | 居中 |
+
+
+### TSearchBarEvent
+#### 类型定义
+
+```dart
+typedef TSearchBarEvent = void Function(String value);
+```
+
+
+### TSearchBarClearEvent
+#### 类型定义
+
+```dart
+typedef TSearchBarClearEvent = bool? Function(String value);
+```
+
+
+### TSearchBarCallBack
+#### 类型定义
+
+```dart
+typedef TSearchBarCallBack = void Function();
+```
+
+
   
\ No newline at end of file
diff --git a/tdesign-site/src/side-bar/README.md b/tdesign-site/src/side-bar/README.md
index 9e5e9cc99..b68ec1183 100644
--- a/tdesign-site/src/side-bar/README.md
+++ b/tdesign-site/src/side-bar/README.md
@@ -645,7 +645,7 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 | controller | TSideBarController? | - | 控制器 |
 | defaultValue | int? | - | 默认值 |
 | height | double? | - | 高度 |
-| key |  | - |  |
+| key | Key? | - | 组件标识,用于区分或保留组件状态。 |
 | loading | bool? | - | 加载效果 |
 | loadingWidget | Widget? | - | 自定义加载动画 |
 | onChanged | ValueChanged? | - | 选中值发生变化(Controller控制) |
@@ -658,8 +658,6 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 | unSelectedColor | Color? | - | 未选中颜色 |
 | value | int? | - | 选项值 |
 
-```
-```
 
 ### TSideBarItem
 #### 默认构造方法
@@ -669,10 +667,20 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 | badge | TBadge? | - | 徽标 |
 | disabled | bool | false | 是否禁用 |
 | icon | IconData? | - | 图标 |
-| key |  | - |  |
+| key | Key? | - | 组件标识,用于区分或保留组件状态。 |
 | label | String | '' | 标签 |
 | textStyle | TextStyle? | - | 标签样式 |
 | value | int | -1 | 值 |
 
 
+### TSideBarStyle
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| normal | - |
+| outline | - |
+
+
   
\ No newline at end of file
diff --git a/tdesign-site/src/slider/README.md b/tdesign-site/src/slider/README.md
index 781aa3218..6e309ab97 100644
--- a/tdesign-site/src/slider/README.md
+++ b/tdesign-site/src/slider/README.md
@@ -772,19 +772,17 @@ onThumbTextTap
 | 参数 | 类型 | 默认值 | 说明 |
 | --- | --- | --- | --- |
 | boxDecoration | Decoration? | - | 自定义盒子样式 |
-| key |  | - |  |
+| key | Key? | - | 组件标识,用于区分或保留组件状态。 |
 | leftLabel | String? | - | 左侧标签 |
-| onChanged | ValueChanged? | - | 滑动变化监听 |
-| onChangeEnd | ValueChanged? | - | 滑动结束监听 |
-| onChangeStart | ValueChanged? | - | 滑动开始监听 |
-| onTap |  Function(Position position, Offset offset, double value)? | - | Thumb 点击事件 位置、坐标、当前值 |
-| onThumbTextTap |  Function(Position position, Offset offset, double value)? | - | Thumb 点击浮标文字 位置、坐标、当前值 |
+| onChanged | ValueChanged? | - | 滑动变化监听 |
+| onChangeEnd | ValueChanged? | - | 滑动结束监听 |
+| onChangeStart | ValueChanged? | - | 滑动开始监听 |
+| onTap | Function(Offset offset, double value)? | - | Thumb 点击事件 坐标、当前值 |
+| onThumbTextTap | Function(Offset offset, double value)? | - | Thumb 点击浮标文字 坐标、当前值 |
 | rightLabel | String? | - | 右侧标签 |
 | sliderThemeData | TSliderThemeData? | - | 样式 |
-| value | RangeValues | - | 默认值 |
+| value | double | - | 默认值 |
 
-```
-```
 
 ### TRangeSlider
 #### 默认构造方法
@@ -792,16 +790,26 @@ onThumbTextTap
 | 参数 | 类型 | 默认值 | 说明 |
 | --- | --- | --- | --- |
 | boxDecoration | Decoration? | - | 自定义盒子样式 |
-| key |  | - |  |
+| key | Key? | - | 组件标识,用于区分或保留组件状态。 |
 | leftLabel | String? | - | 左侧标签 |
 | onChanged | ValueChanged? | - | 滑动变化监听 |
 | onChangeEnd | ValueChanged? | - | 滑动结束监听 |
 | onChangeStart | ValueChanged? | - | 滑动开始监听 |
-| onTap |  Function(Position position, Offset offset, double value)? | - | Thumb 点击事件 位置、坐标、当前值 |
-| onThumbTextTap |  Function(Position position, Offset offset, double value)? | - | Thumb 点击浮标文字 位置、坐标、当前值 |
+| onTap | Function(Position position, Offset offset, double value)? | - | Thumb 点击事件 位置、坐标、当前值 |
+| onThumbTextTap | Function(Position position, Offset offset, double value)? | - | Thumb 点击浮标文字 位置、坐标、当前值 |
 | rightLabel | String? | - | 右侧标签 |
 | sliderThemeData | TSliderThemeData? | - | 样式 |
 | value | RangeValues | - | 默认值 |
 
 
+### Position
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| start | - |
+| end | - |
+
+
   
\ No newline at end of file
diff --git a/tdesign-site/src/stepper/README.md b/tdesign-site/src/stepper/README.md
index df9abaeb5..6c8d1f950 100644
--- a/tdesign-site/src/stepper/README.md
+++ b/tdesign-site/src/stepper/README.md
@@ -126,7 +126,7 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 | disableInput | bool | false | 禁用输入框 |
 | eventController | StreamController? | - | 事件控制器 |
 | inputWidth | double? | - | 禁用全部操作 |
-| key |  | - |  |
+| key | Key? | - | 组件标识,用于区分或保留组件状态。 |
 | max | int | 100 | 最大值 |
 | min | int | 0 | 最小值 |
 | onBlur | VoidCallback? | - | 输入框失去焦点时触发 |
@@ -138,4 +138,71 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 | value | int? | 0 | 值 |
 
 
+### TStepperSize
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| small | - |
+| medium | - |
+| large | - |
+
+
+### TStepperTheme
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| normal | - |
+| filled | - |
+| outline | - |
+
+
+### TStepperIconType
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| remove | - |
+| add | - |
+
+
+### TStepperOverlimitType
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| minus | - |
+| plus | - |
+
+
+### TStepperEventType
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| cleanValue | - |
+
+
+### TStepperOverlimitFunction
+#### 类型定义
+
+```dart
+typedef TStepperOverlimitFunction = void Function(TStepperOverlimitType type);
+```
+
+
+### TTapFunction
+#### 类型定义
+
+```dart
+typedef TTapFunction = void Function();
+```
+
+
   
\ No newline at end of file
diff --git a/tdesign-site/src/steps/README.md b/tdesign-site/src/steps/README.md
index 2209c4d34..e23a2da3f 100644
--- a/tdesign-site/src/steps/README.md
+++ b/tdesign-site/src/steps/README.md
@@ -710,15 +710,13 @@ Vertical Customize Steps 垂直自定义步骤条
 | --- | --- | --- | --- |
 | activeIndex | int | 0 | 步骤条当前激活的索引 |
 | direction | TStepsDirection | TStepsDirection.horizontal | 步骤条方向 |
-| key |  | - |  |
+| key | Key? | - | 组件标识,用于区分或保留组件状态。 |
 | readOnly | bool | false | 步骤条readOnly模式 |
 | simple | bool | false | 步骤条simple模式 |
 | status | TStepsStatus | TStepsStatus.success | 步骤条状态 |
 | steps | List | - | 步骤条数据 |
 | verticalSelect | bool | false | 步骤条垂直自定义步骤条选择模式 |
 
-```
-```
 
 ### TStepsItemData
 #### 默认构造方法
@@ -733,4 +731,28 @@ Vertical Customize Steps 垂直自定义步骤条
 | title | String? | - | 标题 |
 
 
+### TStepsDirection
+#### 简介
+Steps步骤条方向
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| horizontal | - |
+| vertical | - |
+
+
+### TStepsStatus
+#### 简介
+steps步骤条状态
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| success | - |
+| error | - |
+
+
   
\ No newline at end of file
diff --git a/tdesign-site/src/swipe-cell/README.md b/tdesign-site/src/swipe-cell/README.md
index 4d30bc4bc..0947a8d2e 100644
--- a/tdesign-site/src/swipe-cell/README.md
+++ b/tdesign-site/src/swipe-cell/README.md
@@ -346,16 +346,16 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 | --- | --- | --- | --- |
 | cell | Widget | - | 单元格 [TCell] |
 | closeWhenOpened | bool? | true | 当同一组([groupTag])中的一个[TSwipeCell]打开时,是否关闭组中的所有其他[TSwipeCell] |
-| closeWhenTapped | bool? | true | 当同一组([groupTag])中的一个[TSwipeCell]被点击时,是否应该关闭组中的所有[TSwipeCell] |
+| closeWhenTapped | bool? | true | 当同一组([groupTag])中的一个[TSwipeCell]被点击时,是否应该关闭组中的所有[TSwipeCell] [cell]组件被点击时必须传递点击事件,执行`TSwipeCellInherited.of(context)?.cellClick()` |
 | controller | SlidableController? | - | 自定义控制滑动窗口 |
 | direction | Axis? | Axis.horizontal | 可拖动的方向 |
 | disabled | bool? | false | 是否禁用滑动 |
 | dragStartBehavior | DragStartBehavior? | DragStartBehavior.start | 处理拖动开始行为的方式[GestureDetector.dragStartBehavior] |
 | duration | Duration? | const Duration(milliseconds: 200) | 打开关闭动画时长 |
 | groupTag | Object? | - | 组,配置后,[closeWhenOpened]、[closeWhenTapped]才起作用 |
-| key |  | - |  |
+| key | Key? | - | 组件标识,用于区分或保留组件状态。 |
 | left | TSwipeCellPanel? | - | 左侧滑动操作项面板 |
-| onChange |  Function(TSwipeDirection direction, bool open)? | - | 滑动展开事件 |
+| onChange | Function(TSwipeDirection direction, bool open)? | - | 滑动展开事件 |
 | opened | List? | const [false, false] | 默认打开,[left, right] |
 | right | TSwipeCellPanel? | - | 右侧滑动操作项面板 |
 | slidableKey | Key? | - | 滑动组件的 Key |
@@ -363,10 +363,39 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 
 #### 静态方法
 
-| 名称 | 返回类型 | 参数 | 说明 |
+##### TSwipeCell.close
+
+根据[groupTag]关闭[TSwipeCell]
+
+ current:保留当前不关闭
+
+返回类型:`void`
+
+| 参数 | 类型 | 默认值 | 说明 |
+| --- | --- | --- | --- |
+| tag | Object? | - | - |
+| current | SlidableController? | - | - |
+
+
+##### TSwipeCell.of
+
+获取上下文最近的[controller]
+
+返回类型:`SlidableController?`
+
+| 参数 | 类型 | 默认值 | 说明 |
 | --- | --- | --- | --- |
-| close |  |   required Object? tag,  SlidableController? current, | 根据[groupTag]关闭[TSwipeCell]     current:保留当前不关闭 |
-| of |  |   required BuildContext context, | 获取上下文最近的[controller] |
+| context | BuildContext | - | - |
+
+
+### TSwipeDirection
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| right | - |
+| left | - |
 
 
   
\ No newline at end of file
diff --git a/tdesign-site/src/swiper/README.md b/tdesign-site/src/swiper/README.md
index 87b5dda46..1d0d2ed98 100644
--- a/tdesign-site/src/swiper/README.md
+++ b/tdesign-site/src/swiper/README.md
@@ -274,13 +274,20 @@ TDesign风格的Swiper指示器样式,与flutter_swiper的Swiper结合使用
 
 | 参数 | 类型 | 默认值 | 说明 |
 | --- | --- | --- | --- |
-| alignment | Alignment? | - | 当 scrollDirection== Axis.horizontal 时,默认Alignment.bottomCenter |
+| alignment | Alignment? | - | 当 scrollDirection== Axis.horizontal 时,默认Alignment.bottomCenter 当 scrollDirection== Axis.vertical 时,默认Alignment.centerRight |
 | builder | SwiperPlugin | TSwiperPagination.dots | 具体样式 |
-| key | Key? | - |  |
+| key | Key? | - | 组件标识,用于区分或保留组件状态。 |
 | margin | EdgeInsetsGeometry | const EdgeInsets.all(10.0) | 指示器和container之间的距离 |
 
-```
-```
+#### 静态成员
+
+| 名称 | 类型 | 默认值 | 说明 |
+| --- | --- | --- | --- |
+| controls | SwiperPlugin | - | 箭头样式 |
+| dots | SwiperPlugin | - | 圆点样式 |
+| dotsBar | SwiperPlugin | - | 圆角矩形 + 圆点样式 默认宽度20,高度6 |
+| fraction | SwiperPlugin | - | 数字样式 |
+
 
 ### TPageTransformer
 #### 简介
@@ -296,10 +303,23 @@ TD默认PageTransformer
 
 #### 工厂构造方法
 
-| 名称  | 说明 |
-| --- |  --- |
-| TPageTransformer.margin  | 普通margin的卡片式 |
-| TPageTransformer.scaleAndFade  | 缩放或透明的卡片式 |
+##### TPageTransformer.margin
+
+普通margin的卡片式
+
+| 参数 | 类型 | 默认值 | 说明 |
+| --- | --- | --- | --- |
+| margin | double? | 6.0 | 左右间隔 |
+
+
+##### TPageTransformer.scaleAndFade
+
+缩放或透明的卡片式
+
+| 参数 | 类型 | 默认值 | 说明 |
+| --- | --- | --- | --- |
+| fade | double? | 1 | 淡化比例 |
+| scale | double? | 0.8 | 缩放比例 |
 
 
   
\ No newline at end of file
diff --git a/tdesign-site/src/switch/README.md b/tdesign-site/src/switch/README.md
index fa6258b24..a31fbc1cc 100644
--- a/tdesign-site/src/switch/README.md
+++ b/tdesign-site/src/switch/README.md
@@ -227,7 +227,7 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 | closeText | String? | - | 关闭文案 |
 | enable | bool | true | 是否可点击 |
 | isOn | bool | false | 是否打开 |
-| key |  | - |  |
+| key | Key? | - | 组件标识,用于区分或保留组件状态。 |
 | onChanged | OnSwitchChanged? | - | 改变事件 |
 | openText | String? | - | 打开文案 |
 | size | TSwitchSize? | TSwitchSize.medium | 尺寸:大、中、小 |
@@ -240,4 +240,37 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 | type | TSwitchType? | TSwitchType.fill | 类型:填充、文本、加载 |
 
 
+### TSwitchSize
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| large | - |
+| medium | - |
+| small | - |
+
+
+### TSwitchType
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| fill | - |
+| text | - |
+| loading | - |
+| icon | - |
+
+
+### OnSwitchChanged
+#### 简介
+开关改变事件处理
+#### 类型定义
+
+```dart
+typedef OnSwitchChanged = bool Function(bool value);
+```
+
+
   
\ No newline at end of file
diff --git a/tdesign-site/src/tab-bar/README.md b/tdesign-site/src/tab-bar/README.md
index f17afd5e9..e60bc0c42 100644
--- a/tdesign-site/src/tab-bar/README.md
+++ b/tdesign-site/src/tab-bar/README.md
@@ -576,11 +576,11 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 
 | 参数 | 类型 | 默认值 | 说明 |
 | --- | --- | --- | --- |
+| basicType | TBottomTabBarBasicType | - | 基本样式(纯文本、纯图标、图标+文本) |
 | animationCurve | Curve | Curves.easeInOutCubic | 动画曲线 |
 | animationDuration | Duration | const Duration(milliseconds: 300) | 动画时长 |
 | backgroundColor | Color? | - | 背景颜色 (可选) |
 | barHeight | double? | _kDefaultTabBarHeight | tab高度 |
-| basicType | TBottomTabBarBasicType | basicType | 基本样式(纯文本、纯图标、图标+文本) |
 | centerDistance | double? | - | icon与文本中间距离(可选) |
 | componentType | TBottomTabBarComponentType? | TBottomTabBarComponentType.label | 选项样式 默认label |
 | currentIndex | int? | - | 选中的index(可选) |
@@ -588,7 +588,7 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 | dividerHeight | double? | - | 分割线高度(可选) |
 | dividerThickness | double? | - | 分割线厚度(可选) |
 | indicatorAnimation | TBottomTabBarIndicatorAnimation | TBottomTabBarIndicatorAnimation.none | 指示器动画类型 |
-| key |  | - |  |
+| key | Key? | - | 组件标识,用于区分或保留组件状态。 |
 | navigationTabs | List | - | tabs配置 |
 | needInkWell | bool | false | 是否需要水波纹效果 |
 | outlineType | TBottomTabBarOutlineType? | TBottomTabBarOutlineType.filled | 标签栏样式 默认filled |
@@ -600,8 +600,6 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 | useSafeArea | bool | true | 使用安全区域 |
 | useVerticalDivider | bool? | - | 是否使用竖线分隔(如果选项样式为 label,则强制为 false) |
 
-```
-```
 
 ### BadgeConfig
 #### 默认构造方法
@@ -613,8 +611,6 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 | showBadge | bool | - | 是否展示消息 |
 | tBadge | TBadge? | - | 消息样式(未设置但 showBadge 为 true,则默认使用红点) |
 
-```
-```
 
 ### TBottomTabBarTabConfig
 #### 默认构造方法
@@ -632,8 +628,6 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 | unselectedIcon | Widget? | - | 未选中时图标 |
 | unselectTabTextStyle | TextStyle? | - | 文本未选择样式 basicType为text时必填 |
 
-```
-```
 
 ### TBottomTabBarPopUpBtnConfig
 #### 默认构造方法
@@ -644,8 +638,6 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 | onChanged | ValueChanged | - | 统一在 onChanged 中处理各item点击事件 |
 | popUpDialogConfig | TBottomTabBarPopUpShapeConfig? | - | 弹窗UI配置 |
 
-```
-```
 
 ### TBottomTabBarPopUpShapeConfig
 #### 默认构造方法
@@ -659,8 +651,6 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 | popUpWidth | double? | - | 弹窗宽度(不设置,默认为按钮宽度 - 20) |
 | radius | double? | - | panel圆角 默认0 |
 
-```
-```
 
 ### PopUpMenuItem
 #### 默认构造方法
@@ -669,8 +659,51 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 | --- | --- | --- | --- |
 | alignment | AlignmentGeometry | AlignmentDirectional.center | 对齐方式 |
 | itemWidget | Widget? | - | 选项widget |
-| key |  | - |  |
+| key | Key? | - | 组件标识,用于区分或保留组件状态。 |
 | value | String | - | 选项值 |
 
 
+### TBottomTabBarBasicType
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| text | 单层级纯文本标签栏 |
+| iconText | 文本加图标标签栏 |
+| icon | 纯图标标签栏 |
+| expansionPanel | 双层级纯文本标签栏 |
+
+
+### TBottomTabBarComponentType
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| normal | 普通样式 |
+| label | 带胶囊背景的item选中样式 |
+
+
+### TBottomTabBarOutlineType
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| filled | 填充样式 |
+| capsule | 胶囊样式 |
+
+
+### TBottomTabBarIndicatorAnimation
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| none | 无动画,瞬间切换 |
+| linear | 线性滑动:指示器匀速从一个 tab 滑到另一个 |
+| elastic | 弹性动画:指示器先拉伸后收缩 |
+
+
   
\ No newline at end of file
diff --git a/tdesign-site/src/table/README.md b/tdesign-site/src/table/README.md
index fc54bf5a3..8203fc29c 100644
--- a/tdesign-site/src/table/README.md
+++ b/tdesign-site/src/table/README.md
@@ -288,7 +288,7 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 | empty | TTableEmpty? | - | 空表格呈现样式 |
 | footerWidget | Widget? | - | 自定义表尾 |
 | height | double? | - | 表格高度,超出后会出现滚动条 |
-| key |  | - |  |
+| key | Key? | - | 组件标识,用于区分或保留组件状态。 |
 | loading | bool? | false | 加载中状态 |
 | loadingWidget | Widget? | - | 自定义加载中状态 |
 | onCellTap | OnCellTap? | - | 单元格点击事件 |
@@ -300,8 +300,6 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 | stripe | bool? | false | 斑马纹 |
 | width | double? | - | 表格宽度 |
 
-```
-```
 
 ### TTableCol
 #### 默认构造方法
@@ -321,8 +319,6 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 | title | String? | - | 表头标题 |
 | width | double? | - | 列宽 |
 
-```
-```
 
 ### TTableEmpty
 #### 默认构造方法
@@ -333,4 +329,74 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 | text | String? | - | 空状态文字 |
 
 
+### TTableColFixed
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| left | - |
+| right | - |
+| none | - |
+
+
+### TTableColAlign
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| left | - |
+| center | - |
+| right | - |
+
+
+### SelectableFunc
+#### 类型定义
+
+```dart
+typedef SelectableFunc = bool Function(int index, dynamic row);
+```
+
+
+### RowCheckFunc
+#### 类型定义
+
+```dart
+typedef RowCheckFunc = bool Function(int index, dynamic row);
+```
+
+
+### OnCellTap
+#### 类型定义
+
+```dart
+typedef OnCellTap = void Function(int rowIndex, dynamic row, TTableCol col);
+```
+
+
+### OnScroll
+#### 类型定义
+
+```dart
+typedef OnScroll = void Function(ScrollController controller);
+```
+
+
+### OnSelect
+#### 类型定义
+
+```dart
+typedef OnSelect = void Function(List? data);
+```
+
+
+### OnRowSelect
+#### 类型定义
+
+```dart
+typedef OnRowSelect = void Function(int index, bool checked);
+```
+
+
   
\ No newline at end of file
diff --git a/tdesign-site/src/tabs/README.md b/tdesign-site/src/tabs/README.md
index a484905e1..99da59326 100644
--- a/tdesign-site/src/tabs/README.md
+++ b/tdesign-site/src/tabs/README.md
@@ -325,24 +325,22 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 | indicatorPadding | EdgeInsets? | - | 引导padding |
 | indicatorWidth | double? | - | tabBar下标宽度 |
 | isScrollable | bool | false | 是否滚动 |
-| key |  | - |  |
+| key | Key? | - | 组件标识,用于区分或保留组件状态。 |
 | labelColor | Color? | - | tabBar 已选标签颜色 |
 | labelPadding | EdgeInsetsGeometry? | - | tab间距 |
 | labelStyle | TextStyle? | - | 已选label字体 |
-| onTap |  Function(int)? | - | 点击事件 |
+| onTap | Function(int)? | - | 点击事件 |
 | outlineType | TTabBarOutlineType | TTabBarOutlineType.filled | 选项卡样式 |
 | physics | ScrollPhysics? | - | 自定义滑动 |
 | selectedBgColor | Color? | - | 被选中背景色,只有outlineType为capsule时有效 |
 | showIndicator | bool | false | 是否展示引导控件 |
-| tabAlignment |  | - |  |
+| tabAlignment | TabAlignment? | - | - |
 | tabs | List | - | tab数组 |
 | unSelectedBgColor | Color? | - | 未选中背景色,只有outlineType为capsule时有效 |
 | unselectedLabelColor | Color? | - | tabBar未选标签颜色 |
 | unselectedLabelStyle | TextStyle? | - | unselectedLabel字体 |
 | width | double? | - | tabBar宽度 |
 
-```
-```
 
 ### TTab
 #### 默认构造方法
@@ -356,14 +354,12 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 | height | double? | - | tab高度 |
 | icon | Widget? | - | 图标 |
 | iconMargin | EdgeInsetsGeometry | const EdgeInsets.only(bottom: 4.0, right: 4.0) | 图标间距 |
-| key |  | - |  |
+| key | Key? | - | 组件标识,用于区分或保留组件状态。 |
 | outlineType | TTabOutlineType | TTabOutlineType.filled | 选项卡样式 |
 | size | TTabSize | TTabSize.small | 选项卡尺寸 |
 | text | String? | - | 文字内容 |
 | textMargin | EdgeInsetsGeometry? | - | 中间内容宽度 |
 
-```
-```
 
 ### TTabBarView
 #### 默认构造方法
@@ -373,7 +369,39 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 | children | List | - | 子widget列表 |
 | controller | TabController? | - | 控制器 |
 | isSlideSwitch | bool | false | 是否可以滑动切换 |
-| key |  | - |  |
+| key | Key? | - | 组件标识,用于区分或保留组件状态。 |
+
+
+### TTabSize
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| large | - |
+| small | - |
+
+
+### TTabOutlineType
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| filled | 填充样式 |
+| capsule | 胶囊样式 |
+| card | 卡片 |
+
+
+### TTabBarOutlineType
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| filled | 填充样式 |
+| capsule | 胶囊样式 |
+| card | 卡片 |
 
 
   
\ No newline at end of file
diff --git a/tdesign-site/src/tag/README.md b/tdesign-site/src/tag/README.md
index 172fa64f6..056f0bab4 100644
--- a/tdesign-site/src/tag/README.md
+++ b/tdesign-site/src/tag/README.md
@@ -507,6 +507,7 @@ Mark标签
 
 | 参数 | 类型 | 默认值 | 说明 |
 | --- | --- | --- | --- |
+| text | String | - | 标签内容 |
 | backgroundColor | Color? | - | 背景颜色,优先级高于style的backgroundColor |
 | disable | bool | false | 是否为禁用状态 |
 | fixedWidth | double? | - | 标签的固定宽度 |
@@ -517,7 +518,7 @@ Mark标签
 | iconWidget | Widget? | - | 自定义图标内容,需自处理颜色 |
 | isLight | bool | false | 是否为浅色 |
 | isOutline | bool | false | 是否为描边类型,默认不是 |
-| key |  | - |  |
+| key | Key? | - | 组件标识,用于区分或保留组件状态。 |
 | needCloseIcon | bool | false | 关闭图标 |
 | onCloseTap | GestureTapCallback? | - | 关闭图标点击事件 |
 | overflow | TextOverflow? | - | 文字溢出处理 |
@@ -525,18 +526,16 @@ Mark标签
 | shape | TTagShape | TTagShape.square | 标签形状 |
 | size | TTagSize | TTagSize.medium | 标签大小 |
 | style | TTagStyle? | - | 标签样式 |
-| text | String | text | 标签内容 |
 | textColor | Color? | - | 文字颜色,优先级高于style的textColor |
 | theme | TTagTheme? | - | 主题 |
 
-```
-```
 
 ### TSelectTag
 #### 默认构造方法
 
 | 参数 | 类型 | 默认值 | 说明 |
 | --- | --- | --- | --- |
+| text | String | - | 标签内容 |
 | disableSelect | bool | false | 是否禁用选择 |
 | disableSelectStyle | TTagStyle? | - | 不可选标签样式 |
 | fixedWidth | double? | - | 标签的固定宽度 |
@@ -546,7 +545,7 @@ Mark标签
 | isLight | bool | false | 是否为浅色 |
 | isOutline | bool | false | 是否为描边类型,默认不是 |
 | isSelected | bool | false | 是否选中 |
-| key |  | - |  |
+| key | Key? | - | 组件标识,用于区分或保留组件状态。 |
 | needCloseIcon | bool | false | 关闭图标 |
 | onCloseTap | GestureTapCallback? | - | 关闭图标点击事件 |
 | onSelectChanged | ValueChanged? | - | 标签点击,选中状态改变时的回调 |
@@ -554,12 +553,9 @@ Mark标签
 | selectStyle | TTagStyle? | - | 选中的标签样式 |
 | shape | TTagShape | TTagShape.square | 标签形状 |
 | size | TTagSize | TTagSize.medium | 标签大小 |
-| text | String | text | 标签内容 |
 | theme | TTagTheme? | - | 主题 |
 | unSelectStyle | TTagStyle? | - | 未选中标签样式 |
 
-```
-```
 
 ### TTagStyle
 #### 默认构造方法
@@ -575,14 +571,92 @@ Mark标签
 | fontWeight | FontWeight? | - | 字体粗细 |
 | textColor | Color? | - | 文字颜色 |
 
+#### 公开属性
+
+| 属性 | 类型 | 默认值 | 说明 |
+| --- | --- | --- | --- |
+| closeIconColor | Color? | - | 关闭图标颜色 |
+
 
 #### 工厂构造方法
 
-| 名称  | 说明 |
-| --- |  --- |
-| TTagStyle.generateDisableSelectStyle  | 根据主题生成禁用Tag样式 |
-| TTagStyle.generateFillStyleByTheme  | 根据主题生成填充Tag样式 |
-| TTagStyle.generateOutlineStyleByTheme  | 根据主题生成描边Tag样式 |
+##### TTagStyle.generateDisableSelectStyle
+
+根据主题生成禁用Tag样式
+
+| 参数 | 类型 | 默认值 | 说明 |
+| --- | --- | --- | --- |
+| context | BuildContext | - | 上下文,方便获取主题内容 |
+| isLight | bool | - | - |
+| isOutline | bool | - | - |
+| shape | TTagShape | - | - |
+
+
+##### TTagStyle.generateFillStyleByTheme
+
+根据主题生成填充Tag样式
+
+| 参数 | 类型 | 默认值 | 说明 |
+| --- | --- | --- | --- |
+| context | BuildContext | - | 上下文,方便获取主题内容 |
+| theme | TTagTheme? | - | - |
+| light | bool | - | - |
+| shape | TTagShape | - | - |
+
+
+##### TTagStyle.generateOutlineStyleByTheme
+
+根据主题生成描边Tag样式
+
+| 参数 | 类型 | 默认值 | 说明 |
+| --- | --- | --- | --- |
+| context | BuildContext | - | 上下文,方便获取主题内容 |
+| theme | TTagTheme? | - | - |
+| light | bool | - | - |
+| shape | TTagShape | - | - |
+
+
+### TTagTheme
+#### 简介
+Tag展示类型
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| defaultTheme | 默认 |
+| primary | 常规 |
+| warning | 警告 |
+| danger | 危险 |
+| success | 成功 |
+
+
+### TTagSize
+#### 简介
+标签尺寸
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| extraLarge | - |
+| large | - |
+| medium | - |
+| small | - |
+| custom | - |
+
+
+### TTagShape
+#### 简介
+标签形状
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| square | - |
+| round | - |
+| mark | - |
 
 
   
\ No newline at end of file
diff --git a/tdesign-site/src/text/README.md b/tdesign-site/src/text/README.md
index 351b6c18e..a72595af3 100644
--- a/tdesign-site/src/text/README.md
+++ b/tdesign-site/src/text/README.md
@@ -199,8 +199,8 @@ TText.rich测试:
 
 | 参数 | 类型 | 默认值 | 说明 |
 | --- | --- | --- | --- |
+| data | - | - | 以下系统 text 属性,释义请参考系统 [Text] 中注释 |
 | backgroundColor | Color? | - | 背景颜色 |
-| data | null | data | 以下系统 text 属性,释义请参考系统 [Text] 中注释 |
 | font | Font? | - | 字体尺寸,包含 大小size 和 行高height |
 | fontFamily | FontFamily? | - | 字体ttf |
 | fontFamilyUrl | String? | - | 是否禁用懒加载 FontFamily 的能力 |
@@ -208,66 +208,96 @@ TText.rich测试:
 | forceVerticalCenter | bool | false | 是否强制居中 |
 | isInFontLoader | bool | false | 是否在 FontLoader 中使用 |
 | isTextThrough | bool? | false | 是否是横线穿过样式(删除线) |
-| key |  | - |  |
+| key | Key? | - | 组件标识,用于区分或保留组件状态。 |
 | lineThroughColor | Color? | - | 删除线颜色,对应 TestStyle 的 decorationColor |
-| locale |  | - |  |
-| maxLines |  | - |  |
-| overflow |  | - |  |
+| locale | Locale? | - | - |
+| maxLines | int? | - | - |
+| overflow | TextOverflow? | - | - |
 | package | String? | - | 字体包名 |
-| semanticsLabel |  | - |  |
-| softWrap |  | - |  |
-| strutStyle |  | - |  |
+| semanticsLabel | String? | - | - |
+| softWrap | bool? | - | - |
+| strutStyle | StrutStyle? | - | - |
 | style | TextStyle? | - | 自定义的 TextStyle,其中指定的属性,将覆盖扩展的外层属性 |
-| textAlign |  | - |  |
+| textAlign | TextAlign? | - | - |
 | textColor | Color? | - | 文本颜色 |
-| textDirection |  | - |  |
-| textHeightBehavior |  | - |  |
-| textScaleFactor |  | - |  |
-| textWidthBasis |  | - |  |
+| textDirection | TextDirection? | - | - |
+| textHeightBehavior | ui.TextHeightBehavior? | - | - |
+| textScaleFactor | double? | - | - |
+| textWidthBasis | TextWidthBasis? | - | - |
+
+#### 公开属性
+
+| 属性 | 类型 | 默认值 | 说明 |
+| --- | --- | --- | --- |
+| textSpan | InlineSpan? | - | - |
 
 
 #### 工厂构造方法
 
-| 名称  | 说明 |
-| --- |  --- |
-| TText.rich  | 富文本构造方法 |
+##### TText.rich
+
+富文本构造方法
+
+| 参数 | 类型 | 默认值 | 说明 |
+| --- | --- | --- | --- |
+| textSpan | InlineSpan? | - | - |
+| font | Font? | - | 字体尺寸,包含 大小size 和 行高height |
+| fontWeight | FontWeight? | - | 字体粗细 |
+| fontFamily | FontFamily? | - | 字体ttf |
+| textColor | Color? | - | 文本颜色 |
+| backgroundColor | Color? | - | 背景颜色 |
+| isTextThrough | bool? | false | 是否是横线穿过样式(删除线) |
+| lineThroughColor | Color? | - | 删除线颜色,对应 TestStyle 的 decorationColor |
+| package | String? | - | 字体包名 |
+| key | Key? | - | 组件标识,用于区分或保留组件状态。 |
+| style | TextStyle? | - | 自定义的 TextStyle,其中指定的属性,将覆盖扩展的外层属性 |
+| strutStyle | StrutStyle? | - | - |
+| textAlign | TextAlign? | - | - |
+| textDirection | TextDirection? | - | - |
+| locale | Locale? | - | - |
+| softWrap | bool? | - | - |
+| overflow | TextOverflow? | - | - |
+| textScaleFactor | double? | - | - |
+| maxLines | int? | - | - |
+| semanticsLabel | String? | - | - |
+| textWidthBasis | TextWidthBasis? | - | - |
+| textHeightBehavior | ui.TextHeightBehavior? | - | - |
+| forceVerticalCenter | bool | false | 是否强制居中 |
+| isInFontLoader | bool | false | 是否在 FontLoader 中使用 |
+| fontFamilyUrl | String? | - | 是否禁用懒加载 FontFamily 的能力 |
 
-```
-```
 
 ### TTextSpan
 #### 默认构造方法
 
 | 参数 | 类型 | 默认值 | 说明 |
 | --- | --- | --- | --- |
-| children |  | - |  |
-| context |  | - |  |
-| font |  | - |  |
-| fontFamily |  | - |  |
-| fontWeight |  | - |  |
-| isTextThrough |  | false |  |
-| lineThroughColor |  | - |  |
-| mouseCursor |  | - |  |
-| onEnter |  | - |  |
-| onExit |  | - |  |
-| package |  | - |  |
-| recognizer |  | - |  |
-| semanticsLabel |  | - |  |
-| style |  | - |  |
-| text |  | - |  |
-| textColor |  | - |  |
+| children | List? | - | - |
+| context | BuildContext? | - | - |
+| font | Font? | - | - |
+| fontFamily | FontFamily? | - | - |
+| fontWeight | FontWeight? | - | - |
+| isTextThrough | bool? | false | - |
+| lineThroughColor | Color? | - | - |
+| mouseCursor | MouseCursor? | - | - |
+| onEnter | PointerEnterEventListener? | - | - |
+| onExit | PointerExitEventListener? | - | - |
+| package | String? | - | - |
+| recognizer | GestureRecognizer? | - | - |
+| semanticsLabel | String? | - | - |
+| style | TextStyle? | - | - |
+| text | String? | - | - |
+| textColor | Color? | - | - |
 
-```
-```
 
 ### TTextConfiguration
 #### 默认构造方法
 
 | 参数 | 类型 | 默认值 | 说明 |
 | --- | --- | --- | --- |
-| child |  | - |  |
+| child | Widget | - | - |
 | globalFontFamily | FontFamily? | - | 全局字体,kTextNeedGlobalFontFamily=true 时生效 |
-| key |  | - |  |
+| key | Key? | - | 组件标识,用于区分或保留组件状态。 |
 | paddingConfig | TTextPaddingConfig? | - | forceVerticalCenter=true 时,内置 padding 配置 |
 
 
diff --git a/tdesign-site/src/textarea/README.md b/tdesign-site/src/textarea/README.md
index 110a484af..2de7d101d 100644
--- a/tdesign-site/src/textarea/README.md
+++ b/tdesign-site/src/textarea/README.md
@@ -271,7 +271,7 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 | inputDecoration | InputDecoration? | - | 自定义输入框TextField组件样式 |
 | inputFormatters | List? | - | 显示输入内容,如限制长度(LengthLimitingTextInputFormatter(6)) |
 | inputType | TextInputType? | - | 键盘类型,数字、字母 |
-| key |  | - |  |
+| key | Key? | - | 组件标识,用于区分或保留组件状态。 |
 | label | String? | - | 输入框标题 |
 | labelIcon | Widget? | - | 输入框标题图标 |
 | labelStyle | TextStyle? | - | 左侧标签文本样式 |
@@ -298,4 +298,14 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 | width | double? | - | 输入框宽度 |
 
 
+### TTextareaLayout
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| vertical | - |
+| horizontal | - |
+
+
   
\ No newline at end of file
diff --git a/tdesign-site/src/time-counter/README.md b/tdesign-site/src/time-counter/README.md
index f7dec7c0a..d0eb22c69 100644
--- a/tdesign-site/src/time-counter/README.md
+++ b/tdesign-site/src/time-counter/README.md
@@ -620,9 +620,9 @@ TTimeCounter _buildCustomUnitLargeSize(BuildContext context) {
 | controller | TTimeCounterController? | - | 控制器,可控制开始/暂停/继续/重置 |
 | direction | TTimeCounterDirection | TTimeCounterDirection.down | 计时方向,默认倒计时 |
 | format | String | 'HH:mm:ss' | 时间格式,DD-日,HH-时,mm-分,ss-秒,SSS-毫秒(分隔符必须为长度为1的非空格的字符) |
-| key |  | - |  |
+| key | Key? | - | 组件标识,用于区分或保留组件状态。 |
 | millisecond | bool | false | 是否开启毫秒级渲染 |
-| onChange |  Function(int time)? | - | 时间变化时触发回调 |
+| onChange | Function(int time)? | - | 时间变化时触发回调 |
 | onFinish | VoidCallback? | - | 计时结束时触发回调 |
 | size | TTimeCounterSize | TTimeCounterSize.medium | 尺寸 |
 | splitWithUnit | bool | false | 使用时间单位分割 |
@@ -630,14 +630,10 @@ TTimeCounter _buildCustomUnitLargeSize(BuildContext context) {
 | theme | TTimeCounterTheme | TTimeCounterTheme.defaultTheme | 风格 |
 | time | int | - | 必需;计时时长,单位毫秒 |
 
-```
-```
 
 ### TTimeCounterController
 #### 简介
 倒计时组件控制器,可控制开始(`start()`)/暂停(`pause()`)/继续(`resume()`)/重置(`reset([int? time])`)
-```
-```
 
 ### TTimeCounterStyle
 #### 简介
@@ -665,9 +661,69 @@ TTimeCounter _buildCustomUnitLargeSize(BuildContext context) {
 
 #### 工厂构造方法
 
-| 名称  | 说明 |
-| --- |  --- |
-| TTimeCounterStyle.generateStyle  | 生成默认样式 |
+##### TTimeCounterStyle.generateStyle
+
+生成默认样式
+
+| 参数 | 类型 | 默认值 | 说明 |
+| --- | --- | --- | --- |
+| context | BuildContext | - | - |
+| size | TTimeCounterSize? | - | - |
+| theme | TTimeCounterTheme? | - | - |
+| splitWithUnit | bool? | - | - |
+
+
+### TTimeCounterStatus
+#### 简介
+计时组件控制器转态
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| start | 开始 |
+| pause | 暂停 |
+| resume | 继续 |
+| reset | 重置 |
+| idle | 空,默认值 |
+
+
+### TTimeCounterDirection
+#### 简介
+计时组件计时方向
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| down | 倒计时 |
+| up | 正向计时 |
+
+
+### TTimeCounterSize
+#### 简介
+计时组件尺寸
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| small | 小 |
+| medium | 中等 |
+| large | 大 |
+
+
+### TTimeCounterTheme
+#### 简介
+计时组件风格
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| defaultTheme | 默认 |
+| round | 圆形 |
+| square | 方形 |
 
 
   
\ No newline at end of file
diff --git a/tdesign-site/src/toast/README.md b/tdesign-site/src/toast/README.md
index 4556b339e..9b836d4a5 100644
--- a/tdesign-site/src/toast/README.md
+++ b/tdesign-site/src/toast/README.md
@@ -386,18 +386,179 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 
 #### 静态方法
 
-| 名称 | 返回类型 | 参数 | 说明 |
+##### TToast.dismissAll
+
+关闭所有Toast
+
+返回类型:`void`
+
+##### TToast.dismissLoading
+
+关闭加载Toast(向后兼容)
+
+返回类型:`void`
+
+##### TToast.dismissToast
+
+关闭指定的Toast
+
+返回类型:`void`
+
+| 参数 | 类型 | 默认值 | 说明 |
+| --- | --- | --- | --- |
+| toastId | String | - | - |
+
+
+##### TToast.showFail
+
+失败提示Toast
+
+返回类型:`String`
+
+| 参数 | 类型 | 默认值 | 说明 |
+| --- | --- | --- | --- |
+| text | String? | - | - |
+| direction | IconTextDirection | IconTextDirection.horizontal | - |
+| context | BuildContext | - | - |
+| duration | Duration | const Duration(milliseconds: 3000) | - |
+| preventTap | bool? | - | - |
+| backgroundColor | Color? | - | - |
+| maxLines | int? | - | - |
+| textStyle | TextStyle? | - | - |
+| iconSize | double? | - | - |
+| iconColor | Color? | - | - |
+| toastId | String? | - | - |
+
+
+##### TToast.showIconText
+
+带图标的Toast
+
+返回类型:`String`
+
+| 参数 | 类型 | 默认值 | 说明 |
+| --- | --- | --- | --- |
+| text | String? | - | - |
+| icon | IconData? | - | - |
+| direction | IconTextDirection | IconTextDirection.horizontal | - |
+| context | BuildContext | - | - |
+| duration | Duration | const Duration(milliseconds: 3000) | - |
+| preventTap | bool? | - | - |
+| backgroundColor | Color? | - | - |
+| maxLines | int? | - | - |
+| textStyle | TextStyle? | - | - |
+| iconSize | double? | - | - |
+| iconColor | Color? | - | - |
+| toastId | String? | - | - |
+
+
+##### TToast.showLoading
+
+带文案的加载Toast
+
+返回类型:`String`
+
+| 参数 | 类型 | 默认值 | 说明 |
+| --- | --- | --- | --- |
+| context | BuildContext | - | - |
+| text | String? | - | - |
+| duration | Duration | const Duration(seconds: 99999999) | - |
+| preventTap | bool? | - | - |
+| customWidget | Widget? | - | - |
+| backgroundColor | Color? | - | - |
+| textStyle | TextStyle? | - | - |
+| iconSize | double? | - | - |
+| iconColor | Color? | - | - |
+| toastId | String? | - | - |
+
+
+##### TToast.showLoadingWithoutText
+
+不带文案的加载Toast
+
+返回类型:`String`
+
+| 参数 | 类型 | 默认值 | 说明 |
+| --- | --- | --- | --- |
+| context | BuildContext | - | - |
+| duration | Duration | const Duration(seconds: 99999999) | - |
+| preventTap | bool? | - | - |
+| backgroundColor | Color? | - | - |
+| iconSize | double? | - | - |
+| iconColor | Color? | - | - |
+| toastId | String? | - | - |
+
+
+##### TToast.showSuccess
+
+成功提示Toast
+
+返回类型:`String`
+
+| 参数 | 类型 | 默认值 | 说明 |
+| --- | --- | --- | --- |
+| text | String? | - | - |
+| direction | IconTextDirection | IconTextDirection.horizontal | - |
+| context | BuildContext | - | - |
+| duration | Duration | const Duration(milliseconds: 3000) | - |
+| preventTap | bool? | - | - |
+| backgroundColor | Color? | - | - |
+| maxLines | int? | - | - |
+| textStyle | TextStyle? | - | - |
+| iconSize | double? | - | - |
+| iconColor | Color? | - | - |
+| toastId | String? | - | - |
+
+
+##### TToast.showText
+
+普通文本Toast
+
+返回类型:`String`
+
+| 参数 | 类型 | 默认值 | 说明 |
+| --- | --- | --- | --- |
+| text | String? | - | - |
+| context | BuildContext | - | - |
+| duration | Duration | const Duration(milliseconds: 3000) | - |
+| maxLines | int? | - | - |
+| constraints | BoxConstraints? | - | - |
+| preventTap | bool? | - | - |
+| customWidget | Widget? | - | - |
+| backgroundColor | Color? | - | - |
+| textStyle | TextStyle? | - | - |
+| toastId | String? | - | - |
+
+
+##### TToast.showWarning
+
+警告Toast
+
+返回类型:`String`
+
+| 参数 | 类型 | 默认值 | 说明 |
 | --- | --- | --- | --- |
-| dismissAll |  |  | 关闭所有Toast |
-| dismissLoading |  |  | 关闭加载Toast(向后兼容) |
-| dismissToast |  |   required String toastId, | 关闭指定的Toast |
-| showFail |  |   required String? text,  IconTextDirection direction,  required BuildContext context,  Duration duration,  bool? preventTap,  Color? backgroundColor,  int? maxLines,  TextStyle? textStyle,  double? iconSize,  Color? iconColor,  String? toastId, | 失败提示Toast |
-| showIconText |  |   required String? text,  IconData? icon,  IconTextDirection direction,  required BuildContext context,  Duration duration,  bool? preventTap,  Color? backgroundColor,  int? maxLines,  TextStyle? textStyle,  double? iconSize,  Color? iconColor,  String? toastId, | 带图标的Toast |
-| showLoading |  |   required BuildContext context,  String? text,  Duration duration,  bool? preventTap,  Widget? customWidget,  Color? backgroundColor,  TextStyle? textStyle,  double? iconSize,  Color? iconColor,  String? toastId, | 带文案的加载Toast |
-| showLoadingWithoutText |  |   required BuildContext context,  Duration duration,  bool? preventTap,  Color? backgroundColor,  double? iconSize,  Color? iconColor,  String? toastId, | 不带文案的加载Toast |
-| showSuccess |  |   required String? text,  IconTextDirection direction,  required BuildContext context,  Duration duration,  bool? preventTap,  Color? backgroundColor,  int? maxLines,  TextStyle? textStyle,  double? iconSize,  Color? iconColor,  String? toastId, | 成功提示Toast |
-| showText |  |   required String? text,  required BuildContext context,  Duration duration,  int? maxLines,  BoxConstraints? constraints,  bool? preventTap,  Widget? customWidget,  Color? backgroundColor,  TextStyle? textStyle,  String? toastId, | 普通文本Toast |
-| showWarning |  |   required String? text,  IconTextDirection direction,  required BuildContext context,  Duration duration,  bool? preventTap,  Color? backgroundColor,  int? maxLines,  TextStyle? textStyle,  double? iconSize,  Color? iconColor,  String? toastId, | 警告Toast |
+| text | String? | - | - |
+| direction | IconTextDirection | IconTextDirection.horizontal | - |
+| context | BuildContext | - | - |
+| duration | Duration | const Duration(milliseconds: 3000) | - |
+| preventTap | bool? | - | - |
+| backgroundColor | Color? | - | - |
+| maxLines | int? | - | - |
+| textStyle | TextStyle? | - | - |
+| iconSize | double? | - | - |
+| iconColor | Color? | - | - |
+| toastId | String? | - | - |
+
+
+### IconTextDirection
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| horizontal | 横向 |
+| vertical | 竖向 |
 
 
   
\ No newline at end of file
diff --git a/tdesign-site/src/tree-select/README.md b/tdesign-site/src/tree-select/README.md
index dbb2ecb37..0ddcb23ca 100644
--- a/tdesign-site/src/tree-select/README.md
+++ b/tdesign-site/src/tree-select/README.md
@@ -193,15 +193,13 @@ String类型ID(问题3)
 | --- | --- | --- | --- |
 | defaultValue | List | const [] | 初始值,对应options中的value值 |
 | height | double | 336 | 高度 |
-| key |  | - |  |
+| key | Key? | - | 组件标识,用于区分或保留组件状态。 |
 | multiple | bool | false | 支持多选 |
 | onChange | TTreeSelectChangeEvent? | - | 选中值发生变化 |
 | options | List | const [] | 展示的选项列表 |
 | outwardCornerRadius | double | 9 | 一级菜单选中项的外弯折圆角半径,默认为 9 |
 | style | TTreeSelectStyle | TTreeSelectStyle.normal | 一级菜单样式 |
 
-```
-```
 
 ### TSelectOption
 #### 默认构造方法
@@ -216,4 +214,22 @@ String类型ID(问题3)
 | value | dynamic | - | 值 |
 
 
+### TTreeSelectStyle
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| normal | - |
+| outline | - |
+
+
+### TTreeSelectChangeEvent
+#### 类型定义
+
+```dart
+typedef TTreeSelectChangeEvent = void Function(List, int level);
+```
+
+
   
\ No newline at end of file
diff --git a/tdesign-site/src/upload/README.md b/tdesign-site/src/upload/README.md
index f0ae803a5..005a474df 100644
--- a/tdesign-site/src/upload/README.md
+++ b/tdesign-site/src/upload/README.md
@@ -187,7 +187,7 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 | enabledReplaceType | bool? | false | 是否启用replace功能 |
 | files | List | - | 控制展示的文件列表 |
 | height | double? | 80.0 | 图片高度 |
-| key |  | - |  |
+| key | Key? | - | 组件标识,用于区分或保留组件状态。 |
 | max | int | 0 | 用于控制文件上传数量,0为不限制,仅在multiple为true时有效 |
 | mediaType | List | const [TUploadMediaType.image, TUploadMediaType.video] | 支持上传的文件类型,图片或视频 |
 | multiple | bool | false | 是否多选上传,默认false |
@@ -206,4 +206,89 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
 | wrapSpacing | double? | - | 多图布局时的 spacing |
 
 
+### TUploadMediaType
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| image | - |
+| video | - |
+
+
+### TUploadValidatorError
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| overSize | - |
+| overQuantity | - |
+
+
+### TUploadFileStatus
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| success | - |
+| loading | - |
+| error | - |
+| retry | - |
+
+
+### TUploadType
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| add | - |
+| remove | - |
+| replace | - |
+
+
+### TUploadBoxType
+#### 枚举值
+
+
+| 名称 | 说明 |
+| --- | --- |
+| roundedSquare | - |
+| circle | - |
+
+
+### TUploadErrorEvent
+#### 类型定义
+
+```dart
+typedef TUploadErrorEvent = void Function(Object e);
+```
+
+
+### TUploadClickEvent
+#### 类型定义
+
+```dart
+typedef TUploadClickEvent = void Function(int value);
+```
+
+
+### TUploadValueChangedEvent
+#### 类型定义
+
+```dart
+typedef TUploadValueChangedEvent = void Function(List files, TUploadType type);
+```
+
+
+### TUploadValidatorEvent
+#### 类型定义
+
+```dart
+typedef TUploadValidatorEvent = void Function(TUploadValidatorError e);
+```
+
+
   
\ No newline at end of file