diff --git a/coverage/lcov.info b/coverage/lcov.info index aa09329f..913cd03b 100644 --- a/coverage/lcov.info +++ b/coverage/lcov.info @@ -1,3 +1,107 @@ +SF:lib/src/theme/contrast.dart +DA:18,23 +DA:20,23 +DA:22,5 +DA:31,2 +DA:33,2 +DA:35,2 +DA:44,5 +DA:46,5 +DA:48,2 +DA:57,3 +DA:59,3 +DA:61,2 +DA:70,2 +DA:72,2 +DA:74,2 +DA:83,2 +DA:85,2 +DA:87,2 +DA:96,6 +DA:98,6 +DA:100,2 +DA:108,2 +DA:110,2 +DA:112,2 +DA:120,3 +DA:122,3 +DA:124,2 +DA:141,2 +DA:143,2 +DA:145,1 +LF:30 +LH:30 +end_of_record +SF:lib/src/theme/theme_data.dart +DA:22,20 +DA:31,1 +DA:33,1 +DA:34,1 +DA:36,20 +DA:37,20 +DA:39,1 +DA:41,1 +DA:42,1 +DA:44,20 +DA:45,20 +DA:64,40 +DA:71,40 +DA:74,19 +DA:77,19 +DA:79,19 +DA:80,19 +DA:81,19 +DA:82,19 +DA:86,4 +DA:87,4 +DA:88,4 +DA:89,4 +DA:90,4 +DA:91,4 +LF:25 +LH:25 +end_of_record +SF:lib/src/zeta.dart +DA:15,19 +DA:52,15 +DA:53,30 +DA:54,62 +DA:55,4 +DA:56,2 +DA:58,4 +DA:67,2 +DA:68,4 +DA:69,2 +DA:70,2 +DA:77,3 +DA:79,9 +DA:80,9 +DA:81,6 +DA:82,6 +DA:83,6 +DA:90,17 +DA:91,17 +DA:95,1 +DA:96,1 +DA:97,1 +DA:98,1 +DA:101,1 +DA:102,1 +DA:103,2 +DA:104,1 +DA:105,1 +DA:113,1 +DA:115,1 +DA:117,3 +DA:118,3 +DA:119,3 +DA:120,3 +DA:121,3 +DA:122,3 +DA:123,3 +LF:37 +LH:37 +end_of_record SF:lib/src/components/accordion/accordion.dart DA:11,2 DA:38,1 @@ -77,15 +181,15 @@ LF:74 LH:53 end_of_record SF:lib/src/utils/rounded.dart -DA:11,7 -DA:21,12 -DA:22,12 -DA:25,1 -DA:26,3 +DA:11,8 +DA:21,13 +DA:22,13 +DA:25,2 +DA:26,6 DA:27,1 DA:29,1 DA:30,3 -DA:37,218 +DA:37,227 DA:42,8 DA:44,8 DA:45,24 @@ -93,11 +197,11 @@ DA:52,5 DA:57,3 DA:59,3 DA:60,9 -DA:67,13 -DA:69,44 +DA:67,14 +DA:69,46 DA:70,12 DA:73,16 -DA:76,41 +DA:76,43 LF:21 LH:21 end_of_record @@ -2243,21 +2347,21 @@ LF:27 LH:0 end_of_record SF:lib/src/components/icon/icon.dart -DA:17,210 -DA:190,9 -DA:192,9 -DA:193,18 -DA:194,9 -DA:195,9 -DA:196,9 -DA:197,9 -DA:198,9 -DA:199,9 -DA:200,9 -DA:201,9 -DA:202,9 -DA:203,9 -DA:204,9 +DA:17,219 +DA:190,10 +DA:192,10 +DA:193,20 +DA:194,10 +DA:195,10 +DA:196,10 +DA:197,10 +DA:198,10 +DA:199,10 +DA:200,10 +DA:201,10 +DA:202,10 +DA:203,10 +DA:204,10 DA:208,1 DA:210,1 DA:212,3 @@ -2272,15 +2376,15 @@ DA:220,3 DA:221,3 DA:222,3 DA:223,3 -DA:229,7 -DA:230,7 -DA:231,7 -DA:233,7 -DA:234,7 -DA:235,7 -DA:240,9 -DA:241,18 -DA:242,14 +DA:229,8 +DA:230,8 +DA:231,8 +DA:233,8 +DA:234,8 +DA:235,8 +DA:240,10 +DA:241,20 +DA:242,16 LF:38 LH:38 end_of_record @@ -2890,7 +2994,7 @@ DA:334,0 DA:335,0 DA:336,0 DA:337,0 -DA:342,25 +DA:342,26 DA:344,0 LF:141 LH:1 @@ -2938,7 +3042,7 @@ LF:38 LH:38 end_of_record SF:lib/src/components/phone_input/countries.dart -DA:4,25 +DA:4,26 DA:20,0 DA:22,0 DA:23,0 @@ -3458,100 +3562,108 @@ LF:18 LH:0 end_of_record SF:lib/src/components/search_bar/search_bar.dart -DA:8,0 -DA:54,0 -DA:55,0 -DA:56,0 -DA:58,0 -DA:60,0 -DA:61,0 -DA:62,0 -DA:63,0 -DA:64,0 -DA:65,0 -DA:66,0 -DA:67,0 -DA:68,0 -DA:77,0 -DA:79,0 -DA:80,0 -DA:81,0 -DA:82,0 -DA:85,0 -DA:87,0 -DA:88,0 -DA:89,0 -DA:92,0 -DA:94,0 -DA:95,0 -DA:98,0 -DA:100,0 -DA:101,0 -DA:103,0 -DA:104,0 -DA:105,0 -DA:106,0 -DA:107,0 -DA:109,0 -DA:111,0 -DA:113,0 -DA:115,0 -DA:117,0 -DA:118,0 -DA:119,0 -DA:121,0 -DA:122,0 -DA:124,0 -DA:126,0 -DA:135,0 -DA:136,0 -DA:138,0 -DA:139,0 -DA:140,0 -DA:145,0 -DA:146,0 -DA:147,0 -DA:149,0 -DA:151,0 -DA:155,0 -DA:156,0 -DA:158,0 -DA:159,0 -DA:165,0 -DA:167,0 -DA:168,0 -DA:173,0 -DA:175,0 -DA:176,0 -DA:178,0 -DA:179,0 -DA:182,0 -DA:196,0 -DA:197,0 -DA:198,0 -DA:199,0 -DA:200,0 -DA:206,0 -DA:207,0 -DA:208,0 -DA:209,0 -DA:212,0 -DA:213,0 -DA:214,0 -DA:215,0 -DA:218,0 -DA:222,0 -DA:223,0 -DA:224,0 -DA:227,0 -DA:231,0 -DA:232,0 -DA:233,0 -DA:236,0 -DA:237,0 -DA:238,0 -LF:92 -LH:0 +DA:8,2 +DA:66,1 +DA:67,1 +DA:69,1 +DA:71,1 +DA:73,3 +DA:74,3 +DA:75,3 +DA:76,3 +DA:77,3 +DA:78,3 +DA:79,3 +DA:80,3 +DA:81,3 +DA:82,3 +DA:83,3 +DA:84,3 +DA:93,1 +DA:95,1 +DA:96,4 +DA:97,3 +DA:98,3 +DA:101,1 +DA:103,1 +DA:104,3 +DA:105,3 +DA:106,4 +DA:107,4 +DA:111,1 +DA:113,2 +DA:114,1 +DA:117,1 +DA:119,1 +DA:120,2 +DA:122,1 +DA:123,3 +DA:124,1 +DA:125,2 +DA:126,2 +DA:127,1 +DA:129,2 +DA:130,2 +DA:131,6 +DA:133,1 +DA:135,1 +DA:137,2 +DA:139,2 +DA:140,1 +DA:141,7 +DA:143,2 +DA:144,1 +DA:146,1 +DA:148,8 +DA:157,1 +DA:158,1 +DA:160,1 +DA:161,6 +DA:162,1 +DA:168,1 +DA:169,3 +DA:170,3 +DA:172,1 +DA:174,3 +DA:178,2 +DA:179,1 +DA:181,1 +DA:182,3 +DA:188,1 +DA:190,2 +DA:191,1 +DA:197,2 +DA:199,1 +DA:200,3 +DA:202,4 +DA:203,2 +DA:206,1 +DA:220,2 +DA:221,5 +DA:222,2 +DA:223,2 +DA:224,2 +DA:230,1 +DA:231,1 +DA:232,1 +DA:233,1 +DA:236,1 +DA:237,1 +DA:238,1 +DA:239,1 +DA:242,1 +DA:246,1 +DA:247,1 +DA:248,4 +DA:251,1 +DA:255,1 +DA:256,1 +DA:257,4 +DA:260,1 +DA:261,1 +DA:262,1 +LF:100 +LH:100 end_of_record SF:lib/src/components/segmented_control/segmented_control.dart DA:11,0 @@ -3935,47 +4047,6 @@ DA:146,0 LF:50 LH:0 end_of_record -SF:lib/src/zeta.dart -DA:15,18 -DA:52,14 -DA:53,28 -DA:54,58 -DA:55,4 -DA:56,2 -DA:58,4 -DA:67,2 -DA:68,4 -DA:69,2 -DA:70,2 -DA:77,2 -DA:79,6 -DA:80,6 -DA:81,3 -DA:82,3 -DA:83,3 -DA:90,16 -DA:91,16 -DA:95,1 -DA:96,1 -DA:97,1 -DA:98,1 -DA:101,1 -DA:102,1 -DA:103,2 -DA:104,1 -DA:105,1 -DA:113,1 -DA:115,1 -DA:117,3 -DA:118,3 -DA:119,3 -DA:120,3 -DA:121,3 -DA:122,3 -DA:123,3 -LF:37 -LH:37 -end_of_record SF:lib/src/components/snack_bar/snack_bar.dart DA:38,0 DA:82,0 @@ -5685,8 +5756,8 @@ DA:63,1 DA:64,1 DA:65,1 DA:66,7 -DA:75,53 -DA:78,54 +DA:75,56 +DA:78,57 DA:81,3 DA:94,1 DA:96,1 @@ -5746,26 +5817,26 @@ LF:76 LH:76 end_of_record SF:lib/src/theme/color_swatch.dart -DA:15,47 -DA:20,22 +DA:15,49 +DA:20,23 DA:32,3 DA:45,3 DA:48,3 DA:49,3 DA:50,3 -DA:67,23 -DA:68,90 -DA:71,44 -DA:74,18 -DA:77,16 -DA:80,16 -DA:83,22 -DA:86,16 -DA:89,20 -DA:92,16 -DA:95,46 -DA:98,14 -DA:106,36 +DA:67,24 +DA:68,94 +DA:71,46 +DA:74,20 +DA:77,18 +DA:80,18 +DA:83,24 +DA:86,18 +DA:89,22 +DA:92,18 +DA:95,48 +DA:98,16 +DA:106,38 DA:112,4 DA:118,16 DA:124,8 @@ -5774,155 +5845,121 @@ DA:136,4 DA:142,20 DA:148,4 DA:153,8 -DA:165,23 -DA:169,92 -DA:172,84 -DA:175,105 -DA:178,66 -DA:181,21 -DA:184,42 -DA:189,7 -DA:190,7 -DA:191,7 -DA:192,7 -DA:193,7 -DA:194,7 -DA:195,7 -DA:196,7 -DA:197,7 -DA:198,7 -DA:199,7 -DA:200,7 -DA:201,7 -DA:202,7 -DA:203,7 +DA:165,24 +DA:169,96 +DA:172,88 +DA:175,110 +DA:178,69 +DA:181,22 +DA:184,44 +DA:189,8 +DA:190,8 +DA:191,8 +DA:192,8 +DA:193,8 +DA:194,8 +DA:195,8 +DA:196,8 +DA:197,8 +DA:198,8 +DA:199,8 +DA:200,8 +DA:201,8 +DA:202,8 +DA:203,8 LF:50 LH:50 end_of_record -SF:lib/src/theme/contrast.dart -DA:18,22 -DA:20,22 -DA:22,5 -DA:31,2 -DA:33,2 -DA:35,2 -DA:44,5 -DA:46,5 -DA:48,2 -DA:57,3 -DA:59,3 -DA:61,2 -DA:70,2 -DA:72,2 -DA:74,2 -DA:83,2 -DA:85,2 -DA:87,2 -DA:96,6 -DA:98,6 -DA:100,2 -DA:108,2 -DA:110,2 -DA:112,2 -DA:120,3 -DA:122,3 -DA:124,2 -DA:141,2 -DA:143,2 -DA:145,1 -LF:30 -LH:30 -end_of_record SF:lib/src/theme/color_scheme.dart -DA:29,18 -DA:95,18 -DA:151,18 -DA:152,17 -DA:153,17 -DA:154,18 -DA:155,17 -DA:156,17 -DA:157,18 -DA:158,18 -DA:159,18 -DA:160,18 -DA:161,18 -DA:162,18 -DA:163,18 -DA:164,18 -DA:165,18 -DA:166,18 -DA:167,18 -DA:168,18 -DA:169,18 -DA:170,18 -DA:171,18 -DA:172,18 -DA:173,18 -DA:174,18 -DA:175,18 -DA:176,18 -DA:177,18 -DA:178,18 -DA:179,18 -DA:180,18 -DA:181,18 -DA:182,18 -DA:183,18 -DA:184,18 -DA:185,18 -DA:186,18 -DA:187,18 -DA:188,18 -DA:189,18 -DA:190,18 -DA:191,18 -DA:192,18 -DA:193,18 -DA:194,18 -DA:195,18 -DA:196,18 +DA:29,19 +DA:95,19 +DA:151,19 +DA:152,18 +DA:153,18 +DA:154,19 +DA:155,18 +DA:156,18 +DA:157,19 +DA:158,19 +DA:159,19 +DA:160,19 +DA:161,19 +DA:162,19 +DA:163,19 +DA:164,19 +DA:165,19 +DA:166,19 +DA:167,19 +DA:168,19 +DA:169,19 +DA:170,19 +DA:171,19 +DA:172,19 +DA:173,19 +DA:174,19 +DA:175,19 +DA:176,19 +DA:177,19 +DA:178,19 +DA:179,19 +DA:180,19 +DA:181,19 +DA:182,19 +DA:183,19 +DA:184,19 +DA:185,19 +DA:186,19 +DA:187,19 +DA:188,19 +DA:189,19 +DA:190,19 +DA:191,19 +DA:192,19 +DA:193,19 +DA:194,19 +DA:195,19 +DA:196,19 DA:200,1 DA:202,1 DA:204,3 DA:205,3 -DA:208,17 -DA:211,1 -DA:212,1 -DA:213,3 -DA:214,3 -DA:215,3 +DA:208,18 +DA:211,2 +DA:212,2 +DA:213,6 +DA:214,6 +DA:215,6 DA:217,1 DA:218,7 LF:60 LH:60 end_of_record SF:lib/src/theme/colors.dart -DA:16,22 -DA:34,22 -DA:35,22 -DA:36,22 -DA:37,22 -DA:38,22 -DA:39,22 -DA:42,22 -DA:48,22 -DA:50,21 -DA:56,21 -DA:57,22 -DA:58,22 -DA:59,22 -DA:60,22 -DA:61,22 -DA:62,22 -DA:63,22 -DA:64,22 -DA:78,20 -DA:91,20 +DA:16,23 +DA:34,23 +DA:35,23 +DA:36,23 +DA:37,23 +DA:38,23 +DA:39,23 +DA:42,23 +DA:48,23 +DA:50,22 +DA:56,22 +DA:57,23 +DA:58,23 +DA:59,23 +DA:60,23 +DA:61,23 +DA:62,23 +DA:63,23 +DA:64,23 +DA:78,21 +DA:91,21 DA:100,1 DA:101,1 -DA:118,20 -DA:131,20 +DA:118,21 +DA:131,21 DA:141,1 DA:142,1 DA:148,1 @@ -5940,7 +5977,7 @@ DA:169,1 DA:172,1 DA:176,1 DA:180,1 -DA:276,51 +DA:276,54 DA:285,6 DA:292,6 DA:301,6 @@ -5996,10 +6033,10 @@ DA:592,1 DA:593,1 DA:594,1 DA:595,1 -DA:599,22 -DA:607,22 -DA:611,22 -DA:617,22 +DA:599,23 +DA:607,23 +DA:611,23 +DA:617,23 DA:623,3 DA:640,3 DA:641,3 @@ -6015,35 +6052,35 @@ DA:650,3 DA:651,3 DA:652,3 DA:653,8 -DA:660,20 -DA:663,40 +DA:660,21 +DA:663,42 DA:664,3 -DA:670,17 -DA:671,68 -DA:672,68 -DA:673,17 -DA:674,17 -DA:676,17 -DA:678,17 -DA:680,17 -DA:681,17 -DA:682,17 -DA:683,17 -DA:690,5 -DA:691,5 -DA:692,5 -DA:693,5 -DA:694,5 -DA:695,5 -DA:696,5 -DA:697,5 -DA:698,5 -DA:699,5 -DA:700,5 -DA:701,5 -DA:702,5 -DA:703,5 -DA:741,34 +DA:670,18 +DA:671,72 +DA:672,72 +DA:673,18 +DA:674,18 +DA:676,18 +DA:678,18 +DA:680,18 +DA:681,18 +DA:682,18 +DA:683,18 +DA:690,6 +DA:691,6 +DA:692,6 +DA:693,6 +DA:694,6 +DA:695,6 +DA:696,6 +DA:697,6 +DA:698,6 +DA:699,6 +DA:700,6 +DA:701,6 +DA:702,6 +DA:703,6 +DA:741,36 DA:744,2 DA:751,1 DA:752,4 @@ -6065,8 +6102,8 @@ DA:848,1 DA:849,4 DA:855,1 DA:856,4 -DA:863,17 -DA:864,52 +DA:863,18 +DA:864,55 DA:871,1 DA:872,4 DA:879,5 @@ -6144,35 +6181,6 @@ DA:1050,2 LF:243 LH:243 end_of_record -SF:lib/src/theme/theme_data.dart -DA:22,19 -DA:31,1 -DA:33,1 -DA:34,1 -DA:36,19 -DA:37,19 -DA:39,1 -DA:41,1 -DA:42,1 -DA:44,19 -DA:45,19 -DA:64,38 -DA:71,38 -DA:74,18 -DA:77,18 -DA:79,18 -DA:80,18 -DA:81,18 -DA:82,18 -DA:86,3 -DA:87,3 -DA:88,3 -DA:89,3 -DA:90,3 -DA:91,3 -LF:25 -LH:25 -end_of_record SF:lib/src/utils/debounce.dart DA:10,2 DA:11,4 @@ -6236,22 +6244,22 @@ LF:46 LH:46 end_of_record SF:lib/src/utils/nothing.dart -DA:6,326 -DA:8,1 +DA:6,339 +DA:8,2 LF:2 LH:2 end_of_record SF:lib/src/zeta_provider.dart -DA:23,32 -DA:24,34 +DA:23,34 +DA:24,36 DA:32,1 DA:40,1 DA:41,1 -DA:49,16 -DA:59,16 -DA:60,16 -DA:105,17 -DA:106,17 +DA:49,17 +DA:59,17 +DA:60,17 +DA:105,18 +DA:106,18 DA:108,1 DA:110,1 DA:112,3 @@ -6274,19 +6282,19 @@ DA:136,1 DA:137,2 DA:138,1 DA:139,1 -DA:186,17 -DA:188,17 -DA:189,34 -DA:192,102 -DA:195,51 -DA:198,51 -DA:201,51 -DA:204,85 -DA:207,51 -DA:210,51 -DA:216,17 -DA:218,34 -DA:219,17 +DA:186,18 +DA:188,18 +DA:189,36 +DA:192,108 +DA:195,54 +DA:198,54 +DA:201,54 +DA:204,90 +DA:207,54 +DA:210,54 +DA:216,18 +DA:218,36 +DA:219,18 DA:226,1 DA:228,1 DA:231,1 @@ -6297,22 +6305,22 @@ DA:245,3 DA:247,4 DA:249,2 DA:251,2 -DA:258,17 -DA:260,68 -DA:261,16 -DA:262,16 -DA:263,16 -DA:264,16 -DA:265,16 -DA:266,16 -DA:267,48 -DA:269,16 -DA:271,16 -DA:272,64 -DA:274,16 -DA:276,16 -DA:277,64 -DA:279,16 +DA:258,18 +DA:260,72 +DA:261,17 +DA:262,17 +DA:263,17 +DA:264,17 +DA:265,17 +DA:266,17 +DA:267,51 +DA:269,17 +DA:271,17 +DA:272,68 +DA:274,17 +DA:276,17 +DA:277,68 +DA:279,17 DA:284,1 DA:285,1 DA:286,1 @@ -6320,13 +6328,13 @@ DA:287,1 DA:288,1 DA:289,1 DA:290,5 -DA:294,1 -DA:296,1 -DA:297,4 -DA:298,4 -DA:299,4 -DA:300,4 -DA:301,0 +DA:294,2 +DA:296,2 +DA:297,8 +DA:298,8 +DA:299,8 +DA:300,8 +DA:301,4 DA:302,2 DA:303,3 DA:304,3 @@ -6361,7 +6369,7 @@ DA:358,1 DA:360,3 DA:361,3 DA:362,3 -DA:367,16 +DA:367,17 DA:376,0 DA:378,0 DA:380,0 @@ -6373,12 +6381,12 @@ DA:388,0 DA:389,0 DA:390,0 DA:391,0 -DA:395,16 -DA:399,16 -DA:400,16 -DA:402,16 +DA:395,17 +DA:399,17 +DA:400,17 +DA:402,17 DA:405,0 -DA:406,16 +DA:406,17 LF:137 -LH:120 +LH:121 end_of_record diff --git a/example/lib/pages/components/search_bar_example.dart b/example/lib/pages/components/search_bar_example.dart index 9b357322..afaf0520 100644 --- a/example/lib/pages/components/search_bar_example.dart +++ b/example/lib/pages/components/search_bar_example.dart @@ -27,6 +27,10 @@ class _SearchBarExampleState extends State { padding: const EdgeInsets.all(20), child: ZetaSearchBar( onChanged: (value) {}, + textInputAction: TextInputAction.search, + onSubmit: (text) { + print(text); + }, ), ), Padding( diff --git a/lib/src/components/search_bar/search_bar.dart b/lib/src/components/search_bar/search_bar.dart index 9ec0354b..c549b236 100644 --- a/lib/src/components/search_bar/search_bar.dart +++ b/lib/src/components/search_bar/search_bar.dart @@ -12,11 +12,14 @@ class ZetaSearchBar extends StatefulWidget { this.hint, this.initialValue, this.onChanged, + this.onSubmit, this.onSpeechToText, this.disabled = false, this.showLeadingIcon = true, this.showSpeechToText = true, @Deprecated('Use disabled instead. ' 'enabled is deprecated as of 0.11.0') bool enabled = true, + this.focusNode, + this.textInputAction, }); /// Determines the size of the input field. @@ -35,7 +38,13 @@ class ZetaSearchBar extends StatefulWidget { final String? initialValue; /// A callback, which provides the entered text. - final void Function(String?)? onChanged; + final void Function(String? text)? onChanged; + + /// A callback, called when [textInputAction] is performed. + final void Function(String text)? onSubmit; + + /// The type of action button to use for the keyboard. + final TextInputAction? textInputAction; /// A callback, which is invoked when the microphone button is pressed. final Future Function()? onSpeechToText; @@ -51,8 +60,12 @@ class ZetaSearchBar extends StatefulWidget { /// Default is `true`. final bool showSpeechToText; + /// A [FocusNode] for the underlying [TextFormField] + final FocusNode? focusNode; + @override State createState() => _ZetaSearchBarState(); + @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); @@ -65,7 +78,10 @@ class ZetaSearchBar extends StatefulWidget { ..add(StringProperty('initialValue', initialValue)) ..add(ObjectFlagProperty.has('onSpeechToText', onSpeechToText)) ..add(DiagnosticsProperty('showLeadingIcon', showLeadingIcon)) - ..add(DiagnosticsProperty('showSpeechToText', showSpeechToText)); + ..add(DiagnosticsProperty('showSpeechToText', showSpeechToText)) + ..add(DiagnosticsProperty('focusNode', focusNode)) + ..add(ObjectFlagProperty.has('onSubmit', onSubmit)) + ..add(EnumProperty('textInputAction', textInputAction)); } } @@ -87,6 +103,9 @@ class _ZetaSearchBarState extends State { super.didUpdateWidget(oldWidget); _size = widget.size ?? ZetaWidgetSize.large; _shape = widget.shape ?? ZetaWidgetBorder.rounded; + if (oldWidget.initialValue != widget.initialValue) { + _controller.text = widget.initialValue ?? ''; + } } @override @@ -103,9 +122,12 @@ class _ZetaSearchBarState extends State { return ZetaRoundedScope( rounded: widget.shape != ZetaWidgetBorder.sharp, child: TextFormField( + focusNode: widget.focusNode, enabled: !widget.disabled, controller: _controller, keyboardType: TextInputType.text, + textInputAction: widget.textInputAction, + onFieldSubmitted: widget.onSubmit, onChanged: (value) => setState(() => widget.onChanged?.call(value)), style: ZetaTextStyles.bodyMedium, decoration: InputDecoration( @@ -138,6 +160,7 @@ class _ZetaSearchBarState extends State { children: [ if (_controller.text.isNotEmpty && !widget.disabled) ...[ IconButton( + key: const ValueKey('search-clear-btn'), visualDensity: const VisualDensity( horizontal: -4, vertical: -4, @@ -166,6 +189,7 @@ class _ZetaSearchBarState extends State { padding: const EdgeInsets.only(right: ZetaSpacing.minimum), child: widget.showSpeechToText ? IconButton( + key: const ValueKey('speech-to-text-btn'), visualDensity: const VisualDensity( horizontal: -4, vertical: -4, diff --git a/test/src/components/in_page_banner/golden/in_page_banner_negative.png b/test/src/components/in_page_banner/golden/in_page_banner_negative.png index 3088dce8..5f808d48 100644 Binary files a/test/src/components/in_page_banner/golden/in_page_banner_negative.png and b/test/src/components/in_page_banner/golden/in_page_banner_negative.png differ diff --git a/test/src/components/search_bar/golden/search_bar_default.png b/test/src/components/search_bar/golden/search_bar_default.png new file mode 100644 index 00000000..ecfc9ef1 Binary files /dev/null and b/test/src/components/search_bar/golden/search_bar_default.png differ diff --git a/test/src/components/search_bar/golden/search_bar_full.png b/test/src/components/search_bar/golden/search_bar_full.png new file mode 100644 index 00000000..227cf963 Binary files /dev/null and b/test/src/components/search_bar/golden/search_bar_full.png differ diff --git a/test/src/components/search_bar/golden/search_bar_large.png b/test/src/components/search_bar/golden/search_bar_large.png new file mode 100644 index 00000000..7bbccd7d Binary files /dev/null and b/test/src/components/search_bar/golden/search_bar_large.png differ diff --git a/test/src/components/search_bar/golden/search_bar_medium.png b/test/src/components/search_bar/golden/search_bar_medium.png new file mode 100644 index 00000000..43b88a14 Binary files /dev/null and b/test/src/components/search_bar/golden/search_bar_medium.png differ diff --git a/test/src/components/search_bar/golden/search_bar_sharp.png b/test/src/components/search_bar/golden/search_bar_sharp.png new file mode 100644 index 00000000..7bbccd7d Binary files /dev/null and b/test/src/components/search_bar/golden/search_bar_sharp.png differ diff --git a/test/src/components/search_bar/golden/search_bar_small.png b/test/src/components/search_bar/golden/search_bar_small.png new file mode 100644 index 00000000..d5e52b4d Binary files /dev/null and b/test/src/components/search_bar/golden/search_bar_small.png differ diff --git a/test/src/components/search_bar/search_bar_test.dart b/test/src/components/search_bar/search_bar_test.dart new file mode 100644 index 00000000..97ab79b0 --- /dev/null +++ b/test/src/components/search_bar/search_bar_test.dart @@ -0,0 +1,322 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'package:path/path.dart'; +import 'package:zeta_flutter/zeta_flutter.dart'; + +import '../../../test_utils/test_app.dart'; +import '../../../test_utils/tolerant_comparator.dart'; +import '../../../test_utils/utils.dart'; +import 'search_bar_test.mocks.dart'; + +abstract class ISearchBarEvents { + // ignore: unreachable_from_main + void onChange(String? text); + + // ignore: unreachable_from_main + void onSubmit(String text); + + // ignore: unreachable_from_main + Future onSpeech(); +} + +@GenerateNiceMocks([ + MockSpec(), +]) +void main() { + late MockISearchBarEvents callbacks; + + setUpAll(() { + final testUri = Uri.parse(getCurrentPath('search_bar')); + goldenFileComparator = TolerantComparator(testUri, tolerance: 0.01); + }); + + setUp(() { + callbacks = MockISearchBarEvents(); + }); + + group('ZetaSearchBar', () { + testWidgets('renders with default parameters', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: Scaffold( + body: ZetaSearchBar(), + ), + ), + ); + + await tester.pumpAndSettle(); + expect(find.byType(TextFormField), findsOneWidget); + }); + + testWidgets('golden: renders initializes correctly', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: ZetaSearchBar(), + ), + ); + expect(find.byType(ZetaSearchBar), findsOneWidget); + + await expectLater( + find.byType(ZetaSearchBar), + matchesGoldenFile(join(getCurrentPath('search_bar'), 'search_bar_default.png')), + ); + }); + + testWidgets('golden: renders size medium correctly', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: ZetaSearchBar( + size: ZetaWidgetSize.medium, + ), + ), + ); + expect(find.byType(ZetaSearchBar), findsOneWidget); + + await expectLater( + find.byType(ZetaSearchBar), + matchesGoldenFile(join(getCurrentPath('search_bar'), 'search_bar_medium.png')), + ); + }); + + testWidgets('golden: renders size small correctly', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: ZetaSearchBar( + size: ZetaWidgetSize.small, + ), + ), + ); + expect(find.byType(ZetaSearchBar), findsOneWidget); + + await expectLater( + find.byType(ZetaSearchBar), + matchesGoldenFile(join(getCurrentPath('search_bar'), 'search_bar_small.png')), + ); + }); + + testWidgets('golden: renders shape full correctly', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: ZetaSearchBar( + shape: ZetaWidgetBorder.full, + ), + ), + ); + expect(find.byType(ZetaSearchBar), findsOneWidget); + + await expectLater( + find.byType(ZetaSearchBar), + matchesGoldenFile(join(getCurrentPath('search_bar'), 'search_bar_full.png')), + ); + }); + + testWidgets('golden: renders shape sharp correctly', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: ZetaSearchBar( + shape: ZetaWidgetBorder.sharp, + ), + ), + ); + expect(find.byType(ZetaSearchBar), findsOneWidget); + + await expectLater( + find.byType(ZetaSearchBar), + matchesGoldenFile(join(getCurrentPath('search_bar'), 'search_bar_sharp.png')), + ); + }); + + testWidgets('sets initial value correctly', (WidgetTester tester) async { + const initialValue = 'Initial value'; + + await tester.pumpWidget( + const TestApp( + home: Scaffold( + body: ZetaSearchBar(initialValue: initialValue), + ), + ), + ); + + await tester.pumpAndSettle(); + expect(find.text(initialValue), findsOneWidget); + }); + + testWidgets('sets updated initial value correctly', (WidgetTester tester) async { + const initialValue = 'Initial value'; + const updatedValue = 'Updated value'; + + await tester.pumpWidget( + const TestApp( + home: Scaffold( + body: ZetaSearchBar(initialValue: initialValue), + ), + ), + ); + + await tester.pumpAndSettle(); + expect(find.text(initialValue), findsOneWidget); + + await tester.pumpWidget( + const TestApp( + home: Scaffold( + body: ZetaSearchBar(initialValue: updatedValue), + ), + ), + ); + + await tester.pumpAndSettle(); + expect(find.text(updatedValue), findsOneWidget); + }); + + testWidgets('triggers onChanged callback when text is entered', (WidgetTester tester) async { + await tester.pumpWidget( + TestApp( + home: Scaffold( + body: ZetaSearchBar(onChanged: callbacks.onChange), + ), + ), + ); + + await tester.pumpAndSettle(); + await tester.enterText(find.byType(TextFormField), 'New text'); + await tester.pump(); + + verify(callbacks.onChange.call('New text')).called(1); + }); + + testWidgets('triggers onSubmit callback when submit action is performed', (WidgetTester tester) async { + await tester.pumpWidget( + TestApp( + home: Scaffold( + body: ZetaSearchBar(onSubmit: callbacks.onSubmit), + ), + ), + ); + + await tester.pumpAndSettle(); + await tester.enterText(find.byType(TextFormField), 'Submit text'); + await tester.testTextInput.receiveAction(TextInputAction.done); + await tester.pump(); + + verify(callbacks.onSubmit.call('Submit text')).called(1); + }); + + testWidgets('triggers onSpeechToText callback when microphone button is pressed', (WidgetTester tester) async { + when(callbacks.onSpeech.call()).thenAnswer((_) async => 'Speech to text result'); + + await tester.pumpWidget( + TestApp( + home: Scaffold( + body: ZetaSearchBar( + onSpeechToText: callbacks.onSpeech, + ), + ), + ), + ); + + await tester.pumpAndSettle(); + await tester.tap(find.byKey(const ValueKey('speech-to-text-btn'))); + await tester.pump(); + + verify(callbacks.onSpeech.call()).called(1); + }); + + testWidgets('does not allow text input when disabled', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: Scaffold( + body: ZetaSearchBar(disabled: true), + ), + ), + ); + + await tester.pumpAndSettle(); + await tester.enterText(find.byType(TextFormField), 'Disabled input'); + await tester.pump(); + + expect(find.text('Disabled input'), findsNothing); + }); + + testWidgets('leading icon and speech-to-text button visibility', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: Scaffold( + body: ZetaSearchBar( + showLeadingIcon: false, + showSpeechToText: false, + ), + ), + ), + ); + + await tester.pumpAndSettle(); + expect(find.byIcon(ZetaIcons.search), findsNothing); + expect(find.byIcon(ZetaIcons.microphone), findsNothing); + }); + + testWidgets('clear button functionality', (WidgetTester tester) async { + await tester.pumpWidget( + TestApp( + home: Scaffold( + body: ZetaSearchBar(onChanged: callbacks.onChange), + ), + ), + ); + + await tester.pumpAndSettle(); + await tester.enterText(find.byType(TextFormField), 'New text'); + await tester.pump(); + + verify(callbacks.onChange.call('New text')).called(1); + + await tester.tap(find.byKey(const ValueKey('search-clear-btn'))); + await tester.pump(); + + verify(callbacks.onChange.call('')).called(1); + }); + + test('debugFillProperties sets the correct properties', () { + const size = ZetaWidgetSize.medium; + const shape = ZetaWidgetBorder.rounded; + const hint = 'Search here'; + const initialValue = 'Initial value'; + const disabled = true; + const showLeadingIcon = false; + const showSpeechToText = false; + const textInputAction = TextInputAction.search; + + final widget = ZetaSearchBar( + size: size, + shape: shape, + hint: hint, + initialValue: initialValue, + onChanged: callbacks.onChange, + onSubmit: callbacks.onSubmit, + onSpeechToText: callbacks.onSpeech, + disabled: disabled, + showLeadingIcon: showLeadingIcon, + showSpeechToText: showSpeechToText, + textInputAction: textInputAction, + ); + + final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder(); + widget.debugFillProperties(builder); + + expect(builder.findProperty('size'), size); + expect(builder.findProperty('shape'), shape); + expect(builder.findProperty('hint'), hint); + expect(builder.findProperty('enabled'), disabled); + expect(builder.findProperty('initialValue'), initialValue); + expect(builder.findProperty('showLeadingIcon'), showLeadingIcon); + expect(builder.findProperty('showSpeechToText'), showSpeechToText); + expect(builder.findProperty('textInputAction'), textInputAction); + expect(builder.findProperty('focusNode'), null); + expect(builder.findProperty('onChanged'), callbacks.onChange); + expect(builder.findProperty('onSpeechToText'), callbacks.onSpeech); + expect(builder.findProperty('onSubmit'), callbacks.onSubmit); + }); + }); +} diff --git a/test/src/components/search_bar/search_bar_test.mocks.dart b/test/src/components/search_bar/search_bar_test.mocks.dart new file mode 100644 index 00000000..53b0b12b --- /dev/null +++ b/test/src/components/search_bar/search_bar_test.mocks.dart @@ -0,0 +1,56 @@ +// Mocks generated by Mockito 5.4.4 from annotations +// in zeta_flutter/test/src/components/search_bar/search_bar_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'dart:async' as _i3; + +import 'package:mockito/mockito.dart' as _i1; + +import 'search_bar_test.dart' as _i2; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class + +/// A class which mocks [ISearchBarEvents]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockISearchBarEvents extends _i1.Mock implements _i2.ISearchBarEvents { + @override + void onChange(String? text) => super.noSuchMethod( + Invocation.method( + #onChange, + [text], + ), + returnValueForMissingStub: null, + ); + + @override + void onSubmit(String? text) => super.noSuchMethod( + Invocation.method( + #onSubmit, + [text], + ), + returnValueForMissingStub: null, + ); + + @override + _i3.Future onSpeech() => (super.noSuchMethod( + Invocation.method( + #onSpeech, + [], + ), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) as _i3.Future); +} diff --git a/test/src/utils/extensions_test.mocks.dart b/test/src/utils/extensions_test.mocks.dart index 15ccab50..c3b1cbd4 100644 --- a/test/src/utils/extensions_test.mocks.dart +++ b/test/src/utils/extensions_test.mocks.dart @@ -1495,6 +1495,13 @@ class MockZeta extends _i1.Mock implements _i10.Zeta { ), ) as _i6.ZetaThemeData); + @override + bool get rounded => (super.noSuchMethod( + Invocation.getter(#rounded), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + @override _i6.ZetaColors get colors => (super.noSuchMethod( Invocation.getter(#colors), diff --git a/test/test_utils/utils.dart b/test/test_utils/utils.dart index 21ea3356..1afb1742 100644 --- a/test/test_utils/utils.dart +++ b/test/test_utils/utils.dart @@ -1,5 +1,6 @@ import 'dart:io'; +import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; import 'package:path/path.dart'; @@ -9,6 +10,10 @@ String getCurrentPath(String component, {String type = 'components'}) { extension Util on DiagnosticPropertiesBuilder { dynamic finder(String finder) { - return properties.where((p) => p.name == finder).map((p) => p.toDescription()).first; + return properties.where((p) => p.name == finder).map((p) => p.toDescription()).firstOrNull; + } + + dynamic findProperty(String propertyName) { + return properties.firstWhereOrNull((p) => p.name == propertyName)?.value; } }