From f29daff13fc899332f434f3702ad67ed1f83e15f Mon Sep 17 00:00:00 2001 From: Gordon Hayes Date: Fri, 29 Apr 2022 14:58:24 +0200 Subject: [PATCH] docs: v4 migration guide --- .../docs/Flutter/assets/slidable_demo.jpg | Bin 0 -> 12551 bytes .../Flutter/guides/migration_guide_4_0.mdx | 713 ++++++++++++++++++ .../guides/slidable_channel_list_preview.mdx | 212 ++++++ 3 files changed, 925 insertions(+) create mode 100644 docusaurus/docs/Flutter/assets/slidable_demo.jpg create mode 100644 docusaurus/docs/Flutter/guides/migration_guide_4_0.mdx create mode 100644 docusaurus/docs/Flutter/guides/slidable_channel_list_preview.mdx diff --git a/docusaurus/docs/Flutter/assets/slidable_demo.jpg b/docusaurus/docs/Flutter/assets/slidable_demo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4cbda5d738370373027a213a5304783786e74d72 GIT binary patch literal 12551 zcmch7Wq4h?uHX(cGt&-3!_1rp8fL~O4Kp;%%nkDnGh@RIL&MBT!%PiN&N=tpbLahd z^JebM*#0beS+cd3zE57V^tSl62|)cQDI*C00|NlS-Yej31rP*)eJ^mZaPaVONXUrD zNEm3SsAw33nAq=?ke+~q;JwjPF;P-bGEs?heE7g2t{^HXs$ig|X5i-FmYF#P_U|t6 z)(b#`1xo{~fB+*0fTMvypn<*h1K9r%5$X?7|5?DnARwWjVPM}MLV*2GGw&1NP|$D7 z07M8d05~cH>O0RR+6L)AS^t61DQUlBL@2V|o+NU~qEEHLEAxv|Rava{A zd3_t53LOg0{DTYn+N|ZsBZp?rTOe1^H|M8E=mgqh##juS$&4AXeDJwm+O=TDD!-s1 z@SPZ9uA_K^>eXllvo}Hc#W8h>2d5;z-@h`@3zy{Q`+4rikKH^iYFr(EPn&$WeaLH_(dhI{ zy;csndjoKZImJ(y6P)Qcf&S)1Dynd~W5*_&-E)lN{pggydh5nG6c-^%3kxFu?}Drb zmjUP3H-Im6b*f8eZU5RQUJhkd#lJWC3lrGs6^t5fA~=&Rzjm{9U5zcGd4Oo$dPK1s zyZvg%PEZA5Q=0mpn7}sdx#;Vco;JX4$5!}=#YA?NYcv;+EmrNcEqgVcg8crbhs@_% z=IuVt8`BMy(!-3-;;6$P%d|vCla?UE3{w6tfCf|QSW;~o1zrNnKRR6D_hrG9gFgaP z^H1v{e*U#d))UU|9~c3N@UMo<$;##HO8*(d%QdvIN4G3P;<=Koe7E#$DdrO{K_?2g zpATyFbJmwlv7)s-dVF6xy%J0uR*0Ot|6B|5e6fq-!}Qi3(;L7KT>do>R6MhIwf2eU zQ*kAMpaoM?_0_=Gg4}2oRv(!KY1gFnt)KLti~zX(FLjM7@(Vm!5@~6g?9~Wi(T(>+B7oR9yl)^H1G{aB>Wfe{MYvgwp&c9szfLkJ^IS4elhjIf=58Lmrz5m%m`NTbpo>(_Mg!+!T;hv3bRo?3G{Jx*%pZrqHRcoLA z!c&a?PvXB2@XopI4E}k~&=QZqs?9F{fhiB$QI~ci+jEPYjIq0pRgRP<4)$%~zuATd zSMV#V3Oq%XD;QRjqO@1zt9pkA;>qx5|5pnAS7l?64p!%S-D6xqOtrvi68+2if9(Wj z;C&&~z`U`WJ-Mg9^y}zi{DaA?Y5UIWdGL*I*%j+;cI}M#?`)KOSD5k4$qsk^?tdua zKLmX@^BkxEFi3E4FmOmHa7dUxCjPYY0BAIHau^I$Oe{rGGG-QGRt{_+rzj2;8<)6} z;k%vx;|u_U1-tDhk|G zE5BFg&sNz1)+6;Kih8Z~_tm(!gwn&xpM;CDcqN=foXT4z?3mj0Th-UTcW^Te%@=f8 zsOjjiR;z2yKtqX>W*GMyuL$r4N{BH{u#OR$N;>i{qQ;@Y`EU=Akp(bjZ)hhsugUpT zTjxH&!!ONC6s;Rxa0n#H%pBy{Ljmyfex&o6YGEs-gJ}IUztfM zA)XBI-~*1_^TaYlTmY&?4!>#3eDWWk8cBqs!in*IC=Jh{oi^U-=bLhBV)hzWNIT^BRX6YboH!Qq+&bf_ITsA7^> zH@VML863fkh0crAll&i!6~D}DTw^m3jI++CGkApBiCM~XkSW|v_94R6k#XU<6k^(^ zIg5uy*V`ze7|hz(Wh6XXI3uJkaBx&FE$|s|nws2Y-p#E(LKROX?BBjn7FoG=pDfgb z2}?(~9$kKPSVSUenKAFIRN;VrDXrM1qyE+8Tt})c(ioEHqn+LJGvp@KaLuWTp=iwr zTjzw{Hqp0@{I$8*RxfBpsK}t(xGsS5gm&+fb z)4>ly^EYY(FXg{#rNyNqmY2%aqxVY>v$>WV8z!}*yR3q{qOD~X8Yh^?BqgC(I-3V^ zo-=Q>)II~De2wrTt|Wc6!chjCiH*&I4tFV87_CG1ep|_&bg~k-_Bu_As#L9Lbp*w{ zC=9N7obWixhZJOL)orz({oupf2b$q4q~@@7>uNV40oXWokoac|Di7EDF?A3mpG$U_ z5Q~_dA98QFW)5|**{Kklm>$Dxq0V{_YcKqb=-N~q^A~Itfg&_B#4QmgwwJoM@H%8y zSA{Y>A6hP73UQ)l1U`9|7UgR9tTcvi2KAq|Yx=<}bA3?BZZKQK`q=TzhYU_1LP{dq z-jn;C^eWWgs!&cFpeK%%=$%DLa!twRyjuNTjT-t% ziVY}yl$|&OyG32LN@%I5N9Svp$)%h>{!dVg6-e(Je<@nhTwr4@592T}jTYAq4?aaHODyw8+r{>Uu744_a<9&v|^p5WjBq9+!nd`$;^)L~;^KWo%V}%4s(bG?h z$x{q0wSYFRt+Wj{sEISF=7KZpJil~~(?8HE@HI^W7-BOkjXz_>*K2<~n&g8{jkZ5; zXOG-o37HBH`k<(G0bRY(J_V$?_jR{|FrR4J2|0|auyODTSF^6QU_f@O+dVr43=M7*D7b*cxWG&v%Ko^(ZkCT%Hr)=U< zi!K|5RjF#(aGe+N1~_NeJ}S4IDf`tubK9SWjG3P|1Amr$wSGk2<5(wW(3pQw(n@#4 z#?se7)WnsfR$8l7fksrZ7wARVN(7+asYulCFHCRj|0rvI0+e1pop_bsfoVAjHJyvs zSzlh`N6~z#%b!c*$n8&MQsK0u=J1IA7;_S0p(>ng1pXyE|HN8jCQH{$UrmEP-E1l| z<~d5tn67{&75AKf&d)iAL9mx5NHhZT5HY1%&{ufwf!QOD&uB9MufT==3yYkZjyZ zI4&|b>^7`S+;Uy6lUa)`8uwP)R*SC@H6a-ogUyV!E@T?8aMt>|O&Z-Dlzv=Iy1%p%8s55T~8d=Q@S&w}8Ec1AqF`^lDT@gGB! zhUOQ8Q8aQOTEBuXIC(-odCe7jRT+6%K6!pb*@p|A*XsGjvViQfkrwAg)M86M9p$3q z#)&W5$xbK-m{Qbw&g93ua-}*DVkqkQi-x=}oRipfM7K#+Yn)IFr^s?TJjC~5{-M!KKbwq#r ziA(7SxlauoNG0Xo0cJ*!(zW*SGydoo9?mA#;^9{W-?G>FOl6IeLGuM{?kA*3wFtAhO}-_p&*q4OJ42&7DFR?W%8xhB|^ zQpG#_rSe{tas9-2&~BhzF;GuI%w?3eX`5Aj~uregl*;X(=d0 zo5>Y#LfY7*h9vpPge6wd`Yf95VXUNJVe!X`*fEC;g@MaqKB?%z(cf_+m6`?;B*IGQ5kcMaz;kC#Tedq4Y-vH&et%SSpPO=ow zzXwo*agy$x)+XHqR{s<5g&C5+`@Xt!wSQ($D!_B*k4?xss|brxEFgj}7bzgC-eSGz zWW2CZ@Qo20fOkFjO)qY^6LMMHYI#v;hC5wkmt`e$i%A*eF+X1;eJTioc#h@MkOD zO4c_G2sIsYovGOh-Q(3Z75#}Ko=|k?Qx_;Yv|C)QY?`LR~Fnks_nC;TS{orQM%wsuSr+c#qe9netNi< zqM1@sdFT}f2C-Hd#Pv2`n12U=j%2V`%QDctT2s_&vjO6aZr@mw;rAn>FxEm zG&$bbS1z2->z+dWq8IdJdrg%wEe=R2=7e+J#awbpQ$-mRdy;;);~!LBH7G}X&(*q+ z%AZzO=Y(dIYr0ml(|ldpMkk-XXk;`$a;LoCKeRknr!U-tCUty|r@YJG-B~HrcmEX< z009XN1^0ec`R>kwfkOZw(a#$VS5^E_{ z3=?{%*u~V7>U*aD;o!mvy*s#3OuI3V@anjshfG8n;2Z)t+g}<|>MLzd=0Cf)%ju0k z6hgmF1n-2ZIYx8z??Q&5{cc1Eu4(wt6Ts_0+v-Q1q-)^cZ4Nt z3KSR{2YvjkZ;*i?2E2;1A=UU=VTqr~$A^CgAlZ-*JxEDGKvdynj;bf|!sP%uv5FB! z9412wC`Y?GuAe|ZXg0|b#s zZhJjgAK#VTy5G6YkK3p6gquu^%{eD@+ZYKS|EyD+n`<-Lv8=2yjgoFzgofep7W(qN z^0}E+?%?tZX6;3k%)qFIwTu_m80(;o)txRIg8Zll(AQprQc@D{Fm=rotP@5TLlG8~ zN&12P(h6W#Bd@RJn@x8FoiqqCrrI|A24iyd16?b%RuGt(Gn*quID>H@)P>LP%RY5? z_042T72Ow1FUP4m7OC`O%~1uckpG>X+1f;L0Z>lk%r<$+&7S6H5(DaKrTEuGjZX0? zrQpX-21_Nlqi)mM-#Oy&qpKVJU8tWsn(BN8jNA54avB7ZqQ?56)bN1W!eHB0z?M#K z2MnWvz}IszfEzp>be^o&CPHc=0^u9r&QnrdS&SYLRf-(;k~n57ofOu8DbDa@$0CnY zmZe9xiu`dmdz+4@2+>g{M56I~~V=4Y@=7SIf&+tB(Vw2{HuIJ0k0+ z$~t=5^3+HcjXvadXX$}jBNV}6{kt!`%FWfoJR~~pbC1suh)c$;e~zOh>Bo{N6vn6#(8Hss{$=yCWB@d&=DACFvu!X9fZCM#=b}d~= z5m|q+2B2n`ROXLH8eNesE1jMaGPu2lm;zHpmpK3wyFVdw%^-~EJO~U8wkqc9(tWY~ zTrgW7+w#?5>h_xN(ZY$b>gXBq zy>=r2!U2P1hKlj4Dy=LJ20PGN!VE9w~d` zQK{@XXmquGpvwwp7pCjf;24MNk0$Sao7LxlX@2bL;XQs(;Py|(`_FaN zNIUfwk*mP@gENPVaRrJ4VOTzWs#4+wCR;6;4^SD+PiE2&1WaB8JNvyr&KHd)u7oM^ z-|2K0Z@@#a>PrQXdGN#27RqAs| zj)Ms+5-*v^Sw^!Iu@9uq;QxE*;EWae!c+elS>%~{^$7C7j#s9|YlT{gm%%wIclw9# zDa#IUHpwH}%h@RyAG+?)i-wejh?NG)&#Hw1fR;{fm>V%PYRjwa)VEggfO!BVU^^1qPn1=MsK> zD!)_5ju-A4FR>8vU}ufS2qC7xvKp!5(Y3F={T!{XWzP?h^#*tp$0!ZqJk#;=XGRrpa$yg%%J3W-`i#&I}%_R}om_>Q*}}8==09yGI!sV6^IW@R{Y9@LrjJsI$VMEPCan zlJyN>xfcBRS*11rxg1RJ$;D3e7n@D_Ggc<}=WWSs=;Q$&=tQ+Eyb>ya!of%+bw!@c z(VFz2fYnC4KM}`vgNONmCW@X@M06xae^?JfYt#o}cpn}szq7UM1w#21J|?d=lVly{ z7;cl#XR@`(I(T*v^92r($+4h77t(!()%tbVlAtfEd66DD8=xTA6c-s?Qnxcaq3~kS zVbfGI(s*DQs4@pQ1m} z2lEi{aiW9`m+rBe5S*=;^l>_AcF#z-%v0w@)iv>U#m zd*bLz?MjN=Wvh>H_MEXcx6#+z+$#C8V?FYD%Y~5LDwh0`n#djdG`-kUmn;Aga;(TJ zZ1$Nlx=s#*T6OrSxGYCHZz8l_31JZZYz%iVBW*XofNZIezN+7(i?CF%EtxD)E(Djq z&ax`ij36lpisc?r*~nbV=)xzjyaPg!Ue?8orXdl??v%S|TuHIB=!(9spXUZ{xp(H+ z%3G-OYk7ObBAvj9|68vr9hUzr7kmYyVaUi2!7buXj7?d>%$d2XT$2_Z<7?zRX10K@623T< z?{Q_`dap$IlwfRqY)T?H4#x|TOQQx8kVMh(2!n6|M4ydLBjflTX$Pn6Ftl)1Lw}Bx zF@~>JEoX)DW_+GwGL4_&Fh|?mw9&ETs2U+aR7y}R&SJ4KH%b6ZZk=Gm!vPY1ezo*# z5@nyNvqe#)5G1BbQ5z<;;KusWP=uKV>gK-npZnC~IoE}$txeG(RzA+Mp>lCqf=Q|> zuHY9w1T!oz0WZTbT_+xaISA9TcwaVxlfbtFKY*b)qCy6%->DbD9-A{e=JZYd>b17!*9KHp;xnB>_W`Hw2g)O$kc~1sG-*7wR@0RFp01 zi?rRV29HER_%RD^3e=Q;Z1_KJ`ANzVN*`$@XbLg^fl4*OKA-s51GD`Qb3_a}{2%-h zJOOMr32j-c$dEwhuL8%N+?J=(H;^VpuYE!n)peVw|j}REeATW?kQJwW)Xfb zcSO8niC)EWCF3eNXWVW-rK-<{X^AlhQ_5s^Ke)>mTjEUYm`+ii7$(CLk#WPUb^XzS zf>{S+S1{?{$d;jL!9wP`6qA|y%3c<*--O7#x@nFV0Uj+ba$`H#;#EKlAq1A>X!ZRw zc}|pVE*F}vENZwTJge6nf3298<6SJw^iJ~BNFrw5bU|mr>($-K$2b%6^~I!-kluhf zCn57?y|rN*N2+YvF>PA1pvdHMzC~r(VT=z9Zm80bb|F$9z525kn)K&)6;vxKDNK{V z3r`lSNtzI)?|YnW{BG&%ZL=6o^SIEoaZio8kg`Vke~7|=yIIz8<<~es`#IG9ZR9rx zmnDU3u{pttUKXq$&B5!7z|LKYrO>nyZ45Yy!;ep%heCVNtCFyyU0&A`CAO8>Olu9z z33&T4j@>PN=B%RWua9qlw)j}WtLI~_J=0Wb@rCQA7d=`Neu4td_5m^xY0-Uiv7u&RK*zzr5Dyf_5KX|9@`-V4urZ= zkT_-qIM7#5hY63FzjGiI%I$7?oMCmPfy5{>X4Er`(*&v6i~PzLk9@TJ%jw{@PXg9v z=@R1<_h-T>dMG>I5iE!4`OO3^FEQgn2#lQAX@7uMUDlJLj8%VjM z(;MM#=PgHE7RL=eMoWG=i-X&WG4&u907~%C-!JSgK9c-w;b$~ii9A2fqQ7!MT)6&t zR|Osq&oyg+R=o7QaS1DQSbZt&dsuy6`~p+tIAsrv!`9_ACdHySFApdo3lYWQq>hlD zbt(lzXE)MCE~gfY=-^3SXCE^dB+#cJTvQ72Vl+?M+p%#m3d2SgdyfF z?j>Z}{oI4R6~AKVT5|VF1Oe>Xm1Q%(N<3ZsR*EOm<=QEHq80rV4N*zBh0v7x#YV13ol*K2&k~=$_r1vT z-g`0;(glEH+jvAdkCQGV*awDjjuL+JO?yA%xiUgp!@-f%A_<@6bymPjN~&s&l0LLK zjeH}is-{9krl(e*{;q!4p=$pG+nN9k?J>(-V47eeFN<;R#PbI5*O*B8|5Yci8H;A{ zR)T&%JDduOBHR8R64|yIZ)q{AfBl(EF>jD;sN3(7td@kh5mR&zhsr$+jLoO{)v7HP?Bf++^Wj`n-;kxt3babV-WQ0=4N*1_~Gnxu+H^2 zq_Yh?LM@N2PJkn(vc@4k$lch7a9k1~)ui>>jmtp?SnN$G_usiJL|^02l$KUXFi~C) z=oLEAx~Es!SngRS%5s0KFLh@2qx)rnzPuV2Bcj@eEOW9^8sB`ipOY+{I0-ZaSu5my z)o!Pn7tS0RNEd|BQ}CRDZcJd-=&+I!Mbgrdvdzr*s?X})#RXFmO$iH<=`$WK9fp!Q zX#v|{Zs zyEo(G$4J6TcbVeh@FI*EZni@1kF+Acx5@T}bGfo&_!VsOBv>6>7^jL>;)gK&aOHu? zRhlnGyP%9=-t`2v9{+YKPuco7P0Lz@Coaa&jhtVVW@D;_tS5!Raw`nhL)})DDLg;Q zizI~=c%$n)F1QJ0DBaVC=qKfH)7c%?$IN7s?UWlQYD5Ko1wNEC)S>9GM(lTHJdlYmiVPZ=Hiu#y z8Uou-$ADtM#xwNRQ-kgz$InglyQ(OoFlsT=Lv}ic-ydlA_$4k_7Yv3aA_O1dtt4ko zMT06X$supGZqe9`+@o1FaenS*oN%P9LE0J>PaPWqV_%dM!b7EufGyxu{j2N^Fd|Ug zXrrS(_wRKNQC-(4Rwm|`Aj?AC=tV^O(tCMVLPR;u&lJtN7+FrSt_3=7ZUaWTQq5OC z`%t=L(N1Y#@b4fPc4K|>y+;*)Btb%}GkDw{!B9wxMUuAN?`ww$LbKrt)=nxJzhSem-gbg@Dv$){K?Gs zJp#aj2KY0b4FU%I&vdpwBLHXsNHTO5VG$@)MGSIgrS}j3DJz?yv423_<)3%`;9y`v zfE%#O;fbYA65$A35Ofh_j7vhV$+<~UM;GlbLNCa_9|5tlLDH9dW@sQ|Q~%F` zGVde<@|OKs;}>bw>Fsx>;2vnn#NBY|r>MfrWwGb|2H<9OUz%AZ<>qXT7o)SX?DNGc z$?8#NQCy^MmWQrZDpuEA1gxH_wQJZr%7BspJy6~>rLVaj32Wt;&7QZS!r1oGr(fxt zfE|)y1+(L3!VsNG2;zpF90KPfZ6PsYjOkkwXm0vyM{+$9nRCa)jC{lR0lg(8tra1b zwBlP>lhf8NyotIH94!2`?9$%9$g5ABH1g+@qAJ00nYqSdzquX(qj;x!Fc<>PtpTUr zt-fp%-jio+pLA6j;5&VE9_%un<=HGTR}W4^7U2sLSlbgyFkK-wSwk=#Ow_nqg`DOS z0CmE+bpsoXJ(=r*0>n6zoUE>BzP~e3b$)t`1S)Nv9|{AL zoH>tz)zBfn_AiaUC$I|v&LDINkGB`!K5jHu)H%IWK%>U|)+udO1e@QFJl z8I(#MB)suQMoy}8S+&GXCUb`%yV#^RGW_~7`17fGgUi8-7pRhffr@m#NY=D!5_Uz1u zy3?}gy{Zt^98FnV?cdSx7HNJgN#^3k@EdB$RP-cm%W<$(AlJIOB zzVj#3)&{IU8_9ZpdK&8;ssrC%sIq~E07>au&&%`6V). + +### Slidable Channel List Item + +The default slidable channel preview behavior has been removed. We have created a [guide](slidable_channel_list_preview.mdx) showing you how you can easily add this functionality yourself. + +![Slidable demo](../assets/slidable_demo.jpg) + +### Pin Permission + +`pinPermissions` is no longer needed in the **MessageListView** widget. The permissions are automatically fetched for each Stream project. To enable users to pin the message, make sure the pin permissions are granted for different types of users on your [Stream application dashboard](https://dashboard.getstream.io/). + +--- + +## Deprecated Classes + +This section covers all the deprecated classes and widgets in the Stream chat packages. Some of these have also undergone functional changes, for example, **MessageInput** and **ChannelsBloc**. These are discussed in more detail below. + +The majority of the Stream widgets and classes have now been renamed to have a **"Stream"** prefix associated with them. + +Changes: + +- `AttachmentTitle` in favor of `StreamAttachmentTitle` +- `AttachmentUploadStateBuilder` in favor of `StreamAttachmentsUploadStateBuilder` +- `AttachmentWidget` in favor of `StreamAttachmentWidget` +- `AvatarThemeData` in favor of `StreamAvatarThemeData` +- `ChannelAvatar` in favor of `StreamChannelAvatar` +- `ChannelBottomSheet` in favor of `StreamChannelInfoBottomSheet` +- `ChannelHeader` in favor of `StreamChannelHeader` +- `ChannelHeaderTheme` in favor of `StreamChannelHeaderTheme` +- `ChannelHeaderThemeData` in favor of `StreamChannelHeaderThemeData` +- `ChannelInfo` in favor of `StreamChannelInfo` +- `ChannelListHeader` in favor of `StreamChannelListHeader` +- `ChannelListHeaderTheme` in favor of `StreamChannelListHeaderTheme` +- `ChannelListHeaderThemeData` in favor of `StreamChannelListHeaderThemeData` +- `ChannelListView` in favor of `StreamChannelListView` +- `ChannelListViewTheme` in favor of `StreamChannelListViewTheme` +- `ChannelListViewThemeData` in favor of `StreamChannelListViewThemeData` +- `ChannelListHeader` in favor of `StreamChannelListHeader` +- `ChannelListView` in favor of `StreamChannelListView` +- `ChannelName` in favor of `StreamChannelName` +- `ChannelPreview` in favor of `StreamChannelListTile` +- `ChannelPreviewTheme` in favor of `StreamChannelPreviewTheme` +- `ChannelPreviewThemeData` in favor of `StreamChannelPreviewThemeData` +- `ChannelName` in favor of `StreamChannelName` +- `ColorTheme` in favor of `StreamColorTheme` +- `CommandsOverlay` in favor of `StreamCommandsOverlay` +- `ConnectionStatusBuilder` in favor of `StreamConnectionStatusBuilder` +- `DateDivider` in favor of `StreamDateDivider` +- `DeletedMessage` in favor of `StreamDeletedMessage` +- `EmojiOverlay` in favor of `StreamEmojiOverlay` +- `FileAttachment` in favor of `StreamFileAttachment` +- `FullScreenMedia` in favor of `StreamFullScreenMedia` +- `GalleryFooter` in favor of `StreamGalleryFooter` +- `GalleryFooterThemeData` in favor of `StreamGalleryFooterThemeData` +- `GalleryHeader` in favor of `StreamGalleryHeader` +- `GalleryHeaderTheme` in favor of `StreamGalleryHeaderTheme` +- `GalleryHeaderThemeData` in favor of `StreamGalleryHeaderThemeData` +- `GiphyAttachment` in favor of `StreamGiphyAttachment` +- `GradientAvatar` in favor of `StreamGradientAvatar` +- `GroupAvatar` in favor of `StreamGroupAvatar` +- `ImageAttachment` in favor of `StreamImageAttachment` +- `ImageGroup` in favor of `StreamImageGroup` +- `InfoTile` in favor of `StreamInfoTile` +- `MediaListView` in favor of `StreamMediaListView` +- `MessageAction` in favor of `StreamMessageAction` +- `MessageActionsModal` in favor `StreamMessageActionsModal` +- `MessageInput` in favor of `StreamMessageInput` +- `MessageInputTheme` in favor of `StreamMessageInputTheme` +- `MessageInputThemeData` in favor of `StreamMessageInputThemeData` +- `MessageInputState` in favor of `StreamMessageInput` +- `MessageListView` in favor of `StreamMessageListView` +- `MessageListViewTheme` in favor of `StreamMessageListViewTheme` +- `MessageListViewThemeData` in favor of `StreamMessageListViewThemeData` +- `MessageSearchListView` in favor of `StreamMessageSearchListView` +- `MessageSearchListViewTheme` in favor of `StreamMessageSearchListViewTheme` +- `MessageSearchListViewThemeData` in favor of `StreamMessageSearchListViewThemeData` +- `MessageReactionsModal` in favor of `StreamMessageReactionsModal` +- `MessageSearchItem` in favor of `StreamMessageSearchItem` +- `MessageSearchListView` in favor of `StreamMessageSearchListView` +- `MessageText` in favor of `StreamMessageText` +- `MessageWidget` in favor of `StreamMessageWidget` +- `MessageThemeData` in favor of `StreamMessageThemeData` +- `MultiOverlay` in favor of `StreamMultiOverlay` +- `OptionListTile` in favor of `StreamOptionListTile` +- `QuotedMessageWidget` in favor of `StreamQuotedMessageWidget` +- `ReactionBubble` in favor of `StreamReactionBubble` +- `ReactionIcon` in favor of `StreamReactionIcon` +- `ReactionPicker` in favor of `StreamReactionPicker` +- `SendingIndicator` in favor of `StreamSendingIndicator` +- `SystemMessage` in favor of `StreamSystemMessage` +- `TextTheme` in favor of `StreamTextTheme` +- `ThreadHeader` in favor of `StreamThreadHeader` +- `TypingIndicator` in favor of `StreamTypingIndicator` +- `UnreadIndicator` in favor of `SteamUnreadIndicator` +- `UploadProgressIndicator` in favor of `StreamUploadProgressIndicator` +- `UrlAttachment` in favor of `StreamUrlAttachment` +- `UserAvatar` in favor of `StreamUserAvatar` +- `UserItem` in favor of `StreamUserItem` +- `UserListView` in favor of `StreamUserListView` +- `UserListViewTheme` in favor of `StreamUserListViewTheme` +- `UserListViewThemeData` in favor of `StreamUserListViewThemeData` +- `UserMentionTile` in favor of `StreamUserMentionTile` +- `UserMentionsOverlay` in favor of `StreamUserMentionsOverlay` +- `VideoAttachment` in favor of `StreamVideoAttachment` +- `VideoService` in favor of `StreamVideoService` +- `VideoThumbnailImage` in favor of `StreamVideoThumbnailImage` +- `VisibleFootnote` in favor of `StreamVisibleFootnote` + +## ChannelListView to StreamChannelListView + +The `ChannelListView` widget has been deprecated, and it is now recommended to use `StreamChannelListView`. + +Version 4 of the Stream Chat Flutter packages introduces a new controller called, `StreamChannelListController`. This controller manages the content for a channel list; it lets you perform tasks such as: + +- Load initial data. +- Use channel events handlers. +- Load more data using `loadMore`. +- Replace the previously loaded channels. +- Return/Create a new channel and start watching it. +- Pause and Resume all subscriptions added to this composite. + +### ChannelsBloc to StreamChannelListController + +The `ChannelsBloc` widget should be replaced with `StreamChannelListController`. This controller provides all the functionality needed to query and manipulate channel data previously accessible through `ChannelsBloc`. + +### StreamChannelListView Examples + +Let's explore some examples of the functional differences when using the new `StreamChannelListView`. + +The **StreamChannelListController** provides various methods, such as: + +- **deleteChannel** +- **loadMore** +- **muteChannel** +- **deleteChannel** + +For a complete list with additional information, see the code documentation. + +#### Basic Use + +The following code demonstrates the old way of creating a **ChannelListPage**, that displays a list of channels: + +```dart +class ChannelListPage extends StatelessWidget { + const ChannelListPage({ + Key? key, + }) : super(key: key); + + @override + // ignore: prefer_expression_function_bodies + Widget build(BuildContext context) { + return Scaffold( + body: ChannelsBloc( + child: ChannelListView( + filter: Filter.in_( + 'members', + [StreamChat.of(context).currentUser!.id], + ), + sort: const [SortOption('last_message_at')], + limit: 20, + channelWidget: const ChannelPage(), + ), + ), + ); + } +} +``` + +In **v4** this can now be achieved with the following: + +```dart +class ChannelListPage extends StatefulWidget { + const ChannelListPage({ + Key? key, + required this.client, + }) : super(key: key); + + final StreamChatClient client; + + @override + State createState() => _ChannelListPageState(); +} + +class _ChannelListPageState extends State { + late final _controller = StreamChannelListController( + client: widget.client, + filter: Filter.in_( + 'members', + [StreamChat.of(context).currentUser!.id], + ), + sort: const [SortOption('last_message_at')], + ); + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) => Scaffold( + body: RefreshIndicator( + onRefresh: _controller.refresh, + child: StreamChannelListView( + controller: _controller, + onChannelTap: (channel) => Navigator.push( + context, + MaterialPageRoute( + builder: (_) => StreamChannel( + channel: channel, + child: const ChannelPage(), + ), + ), + ), + ), + ), + ); +} +``` + +As you can see, the **ChannelsBloc** has been replaced with a **StreamChannelListController**, where the **filter**, **limit**, and **sort** arguments can be set. The above code also demonstrates how to refresh the channel list by calling `_controller.refresh()`. + +## MessageSearchListView to StreamMessageSearchListView + +The `MessageSearchListView` widget has been deprecated, and it is now recommended to use `StreamMessageSearchListView`. + +Version 4 of the Stream Chat Flutter packages introduces a new controller called, `StreamMessageSearchListController`. This controller manages the content when searching for a message; it lets you perform tasks such as: + +- Load initial data. +- Set filters and search terms. +- Load more data using `loadMore`. +- Refresh data. + +### MessageSearchBloc to StreamMessageSearchListController + +The `MessageSearchBloc` widget should be replaced with a `StreamMessageSearchListController`. This controller provides all the functionality needed to query and manipulate message search data previously accessible through `MessageSearchBloc`. + +### StreamMessageSearchListView Example + +The following code demonstrates the old way of searching for messages: + +```dart +class SearchExample extends StatelessWidget { + const SearchExample({ + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return MessageSearchBloc( + child: MessageSearchListView( + showErrorTile: true, + messageQuery: 'message query', + filters: Filter.in_('members', const ['user-id']), + sortOptions: const [ + SortOption( + 'created_at', + direction: SortOption.ASC, + ), + ], + pullToRefresh: false, + limit: 30, + emptyBuilder: (context) => const Text('Nothing to show'), + itemBuilder: (context, messageResponse) { + /// Return widget + } + onItemTap: (messageResponse) { + /// Handle on tap + } + ), + ); + } +} +``` + +In **v4**, this can now be achieved with the following: + +```dart +class SearchExample extends StatefulWidget { + const SearchExample({ + Key? key, + }) : super(key: key); + + @override + State createState() => _SearchExampleState(); +} + +class _SearchExampleState extends State { + late final StreamMessageSearchListController _messageSearchListController = + StreamMessageSearchListController( + client: StreamChat.of(context).client, + filter: Filter.in_('members', [StreamChat.of(context).currentUser!.id]), + limit: 5, + searchQuery: '', + sort: [ + const SortOption( + 'created_at', + direction: SortOption.ASC, + ), + ], + ); + + search() { + _messageSearchListController.searchQuery = 'search-value'; + _messageSearchListController.doInitialLoad(); + } + + @override + dispose() { + _messageSearchListController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return StreamMessageSearchListView( + controller: _messageSearchListController, + emptyBuilder: (context) => const Text('Nothing to show'), + itemBuilder: ( + context, + messageResponses, + index, + defaultWidget, + ) { + return defaultWidget.copyWith(); // modify default widget + }); + } +} +``` + +## UserListView to StreamUserListView + +The `UserListView` widget has been deprecated, and it is now recommended to use `StreamUserListView`. + +Version 4 of the Stream Chat Flutter packages introduces a new controller called, `StreamUserListController`. This controller manages the content when retrieving Stream users; it let's you perform tasks, such as: + +- Load data. +- Set filters. +- Refresh data. + +### UsersBloc to StreamUserListController + +The `UsersBloc` widget should be replaced with a `StreamUserListController`. This controller provides all the functionality needed to query and manipulate user data previously accessible through `UsersBloc`. + +### StreamUserListView Example + +The following code demonstrates the old way of displaying all users: + +```dart +class UsersExample extends StatelessWidget { + const UsersExample({ + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return UsersBloc( + child: UserListView( + groupAlphabetically: true, + onUserTap: (user, _) { + /// Handle on tap + }, + limit: 25, + filter: Filter.and([ + Filter.autoComplete('name', 'some-name'), + Filter.notEqual('id', StreamChat.of(context).currentUser!.id), + ]), + sort: const [ + SortOption( + 'name', + direction: 1, + ), + ], + ), + ); + } +} +``` + +In **v4**, this can now be achieved with the following: + +```dart +class UsersExample extends StatefulWidget { + const UsersExample({ + Key? key, + }) : super(key: key); + + @override + State createState() => _UsersExampleState(); +} + +class _UsersExampleState extends State { + late final userListController = StreamUserListController( + client: StreamChat.of(context).client, + limit: 25, + filter: Filter.and([ + Filter.notEqual('id', StreamChat.of(context).currentUser!.id), + ]), + sort: [ + const SortOption( + 'name', + direction: 1, + ), + ], + ); + + void _load() { + userListController.filter = Filter.and([ + Filter.autoComplete('name', 'some-name'), + Filter.notEqual('id', StreamChat.of(context).currentUser!.id), + ]); + userListController.doInitialLoad(); + } + + @override + dispose() { + userListController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return StreamUserListView( + controller: userListController, + onUserTap: (user) { + /// Handle on tap + }, + emptyBuilder: (context) => const Text('Nothing to show'), + itemBuilder: ( + context, + users, + index, + defaultWidget, + ) { + return defaultWidget.copyWith(); // modify default widget + }, + ); + } +} +``` + +## MessageInput to StreamMessageInput + +The `MessageInput` widget has been deprecated, and it is now recommended to use `StreamMessageInput`. + +Version 4 of the Stream Chat Flutter packages introduces a new controller called, `MessageInputController`. This controller maintains the state of the message input and exposes various methods to allow you to customize and manipulate the underlying **Message** value. + +Creating a separate controller allows easier control over the message input content by moving logic out of the deprecated `MessageInput` and into the controller. This controller can then be created, managed, and exposed in whatever way you like. + +The widget is also separated into smaller components: `StreamCountDownButton`, `StreamAttachmentPicker`, etc. + +> ❗The `MessageInputController` is exposed by the **stream_chat_flutter_core** package. This allows you to use the controller even if you're not using the UI components. + +As a result of this extra control, it is no longer needed for the new `StreamMessageInput` widget to expose these `MessageInput` arguments: + +- `parentMessage`: parent message in case of a thread +- `editMessage`: message to edit +- `initialMessage`: message to start with +- `quotedMessage`: message to quote/reply +- `onQuotedMessageCleared`: callback for clearing quoted message +- `textEditingController`: the text controller of the text field + +The following arguments are newly introduced to the `StreamMessageInput`, and are not available on the old `MessageInput`: + +- `messageInputController`: the controller for the message input +- `attachmentsPickerBuilder`: builder for bottom sheet when attachment picker is opened +- `sendButtonBuilder`: builder for creating send button +- `validator`: a callback function that validates the message +- `restorationId`: restoration ID to save and restore the state of the MessageInput +- `enableSafeArea`: wraps the **StreamMessageInput** widget with a **SafeArea** widget +- `elevation`: elevation of the **StreamMessageInput** widget +- `shadow`: **Shadow** for the **StreamMessageInput** widget + +### StreamMessageInput Examples + +Let's explore some examples of the functional differences when using the new `StreamMessageInput`. + +#### Basic Use + +Unless you want to programmatically manipulate the value of the message input, then there is no difference in how you would use the message input widget. + +The following code demonstrates the old way of creating a **ChannelPage** widget that displays a chat screen: + +```dart +class ChannelPage extends StatelessWidget { + const ChannelPage({ + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: const ChannelHeader(), + body: Column( + children: const [ + Expanded( + child: MessageListView(), + ), + MessageInput(), + ], + ), + ); + } +} +``` + +In **v4** this is the same, the only difference being that all the Stream widgets are now prefixed with **Stream**. For example, **MessageListView** becomes **StreamMessageListView**, and so forth. + +```dart +class ChannelPage extends StatelessWidget { + const ChannelPage({ + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext context) => Scaffold( + appBar: const StreamChannelHeader(), + body: Column( + children: const [ + Expanded( + child: StreamMessageListView(), + ), + StreamMessageInput(), + ], + ), + ); +} +``` + +However, you can optionally pass in a **MessageInputController** in the **StreamMessageInput**, which gives extra control over the message input value. + +#### Thread Page + +The following code demonstrates the old way of creating a thread page: + +```dart +class ThreadPage extends StatelessWidget { + const ThreadPage({ + Key? key, + this.parent, + }) : super(key: key); + + final Message? parent; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: ThreadHeader( + parent: parent!, + ), + body: Column( + children: [ + Expanded( + child: MessageListView( + parentMessage: parent, + ), + ), + MessageInput( + parentMessage: parent, + ), + ], + ), + ); + } +} +``` + +In **v4** the only difference is the **Stream** prefix and the way that the parent message is passed to the message input: + +```dart +class ThreadPage extends StatelessWidget { + const ThreadPage({ + Key? key, + this.parent, + }) : super(key: key); + + final Message? parent; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: StreamThreadHeader( + parent: parent!, + ), + body: Column( + children: [ + Expanded( + child: StreamMessageListView( + parentMessage: parent, + ), + ), + StreamMessageInput( + messageInputController: MessageInputController( + message: Message(parentId: parent!.id), + ), + ), + ], + ), + ); + } +} +``` + +To send a thread message, you need to specify the message's parent ID for which you're creating a thread. + +#### Reply/Quote Message + +The following code demonstrates the old way of replying to a message: + +```dart +... + +void _reply(Message message) { + setState(() => _quotedMessage = message); +} + +... + +MessageInput + quotedMessage: _quotedMessage, + onQuotedMessageCleared: () { + setState(() => _quotedMessage = null); + }, +), +``` + +To reply to a message in **v4**: + +```dart +... + +void _reply(Message message) { + _messageInputController.quotedMessage = message; +} + +... + +StreamMessageInput( + messageInputController: _messageInputController, +), +``` + +The controller makes it much simpler to dynamically modify the message input. + +## Stream Chat Flutter Core + +Various changes have been made to the Core package, most notably, the indroduction of all of the controllers mentioned above: + +These controllers replace the business logic implementations (Bloc). Please note that this is not related to the well-known Flutter Bloc package, but instead refers to the naming we used for our business logic components. + +In this version we're introducing controllers in place of their bloc counterparts: +**StreamChannelListController** in favor of **ChannelsBloc** +**StreamMessageSearchListController** in favor of **MessageSearchBloc** +**StreamUserListController** in favor of **UsersBloc** + +The Bloc components are deprecated in v4.0.0 but can still be used. They will be removed in the next major release (v5.0.0). + +Additionally, we also now have the **StreamMessageInputController**, as discussed above. This can be used outside of our UI package as well. + +Finally, the following Core builders are also deprecated as their functionality can be replaced using their controller counterparts: + +- ChannelListCore +- MessageSearchListCore +- UserListCore diff --git a/docusaurus/docs/Flutter/guides/slidable_channel_list_preview.mdx b/docusaurus/docs/Flutter/guides/slidable_channel_list_preview.mdx new file mode 100644 index 00000000..9d57e709 --- /dev/null +++ b/docusaurus/docs/Flutter/guides/slidable_channel_list_preview.mdx @@ -0,0 +1,212 @@ +--- +id: slidable_channel_list_preview +sidebar_position: 14 +title: Slidable Channel List Preview +--- + +Slidable Channel List Preview + +### Introduction + +The default slidable behavior within the channel list has been removed in v4 of the Stream Chat Flutter SDK. +This guide will show you how you can easily add this functionality yourself. + +Please see our [full v4 migration guide](migration_guide_4_0.mdx) if you're migrating from an earlier version of the Stream Chat Flutter SDK. + +![Slidable demo](../assets/slidable_demo.jpg) + +### Prerequisites + +This guide assumes you are familiar with the Stream Chat SDK. +If you're new to Stream Chat Flutter, we recommend looking at our [getting started tutorial](https://getstream.io/chat/flutter/tutorial/). + +**Dependencies:** + +```dart +dependencies: + flutter: + sdk: flutter + stream_chat_flutter: ^4.0.0 + flutter_slidable: ^1.2.0 +``` + +⚠️ Note: The examples shown in this guide use the above packages and versions. + +### Example Code - Custom Stream Channel Item Builder + +In this example, you are doing a few important things in the ChannelListPage widget. You're: + +- Using the **flutter_slidable** package to easily add slide functionality. +- Passing in the `itemBuilder` argument for the **StreamChannelListView** widget. This gives access to the current **BuildContext**, **Channel**, and **StreamChannelListTile**, and allows you to create, or customize, the stream channel list tiles. +- Returning a Slidable widget with two CustomSlidableAction widgets - to delete a channel and show more options. These widgets come from the flutter_slidable package. +- Adding `onPressed` behaviour to call `showConfirmationDialog` and `showChannelInfoModalBottomSheet`. These methods come from the **stream_chat_flutter** package. They have a few different on-tap callbacks you can supply, for example, `onViewInfoTap`. Alternatively, you can create custom dialogs from scratch. +- Using the **StreamChannelListController** to perform actions, such as, `deleteChannel`. + +```dart +import 'package:flutter/material.dart'; +import 'package:flutter_slidable/flutter_slidable.dart'; +import 'package:stream_chat_flutter/stream_chat_flutter.dart'; + +void main() async { + final client = StreamChatClient( + 's2dxdhpxd94g', + ); + + await client.connectUser( + User(id: 'super-band-9'), + '''eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3VwZXItYmFuZC05In0.0L6lGoeLwkz0aZRUcpZKsvaXtNEDHBcezVTZ0oPq40A''', + ); + + runApp( + MyApp( + client: client, + ), + ); +} + +class MyApp extends StatelessWidget { + const MyApp({ + Key? key, + required this.client, + }) : super(key: key); + + final StreamChatClient client; + + @override + Widget build(BuildContext context) { + return MaterialApp( + builder: (context, child) => StreamChat( + client: client, + child: child, + ), + home: ChannelListPage( + client: client, + ), + ); + } +} + +class ChannelListPage extends StatefulWidget { + const ChannelListPage({ + Key? key, + required this.client, + }) : super(key: key); + + final StreamChatClient client; + + @override + State createState() => _ChannelListPageState(); +} + +class _ChannelListPageState extends State { + late final _controller = StreamChannelListController( + client: widget.client, + filter: Filter.in_( + 'members', + [StreamChat.of(context).currentUser!.id], + ), + sort: const [SortOption('last_message_at')], + ); + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) => Scaffold( + body: SlidableAutoCloseBehavior( + child: RefreshIndicator( + onRefresh: _controller.refresh, + child: StreamChannelListView( + controller: _controller, + itemBuilder: (context, channel, tile) { + final chatTheme = StreamChatTheme.of(context); + final backgroundColor = chatTheme.colorTheme.inputBg; + final canDeleteChannel = channel.ownCapabilities + .contains(PermissionType.deleteChannel); + return Slidable( + groupTag: 'channels-actions', + endActionPane: ActionPane( + extentRatio: canDeleteChannel ? 0.40 : 0.20, + motion: const BehindMotion(), + children: [ + CustomSlidableAction( + onPressed: (_) { + showChannelInfoModalBottomSheet( + context: context, + channel: channel, + onViewInfoTap: () { + Navigator.pop(context); + // Navigate to info screen + }, + ); + }, + backgroundColor: backgroundColor, + child: const Icon(Icons.more_horiz), + ), + if (canDeleteChannel) + CustomSlidableAction( + backgroundColor: backgroundColor, + child: StreamSvgIcon.delete( + color: chatTheme.colorTheme.accentError, + ), + onPressed: (_) async { + final res = await showConfirmationDialog( + context, + title: 'Delete Conversation', + question: + 'Are you sure you want to delete this conversation?', + okText: 'Delete', + cancelText: 'Cancel', + icon: StreamSvgIcon.delete( + color: chatTheme.colorTheme.accentError, + ), + ); + if (res == true) { + await _controller.deleteChannel(channel); + } + }, + ), + ], + ), + child: tile, + ); + }, + onChannelTap: (channel) => Navigator.push( + context, + MaterialPageRoute( + builder: (_) => StreamChannel( + channel: channel, + child: const ChannelPage(), + ), + ), + ), + ), + ), + ), + ); +} + +class ChannelPage extends StatelessWidget { + const ChannelPage({ + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext context) => Scaffold( + appBar: const StreamChannelHeader(), + body: Column( + children: const [ + Expanded( + child: StreamMessageListView(), + ), + StreamMessageInput(), + ], + ), + ); +} +``` + +The above is the complete sample, and all you need for a basic implementation.