Dart Flutter ile Whatsapp Responsive UI Yazalım

Gauloran

Moderasyon Ekibi Lideri
7 Tem 2013
8,210
4
697
Merhaba bu konuda Flutter ile whatsapp ui yazmaya çalışacağız hem mobil hem de web için responsive bir tasarım oluşturmayı hedefliyoruz işlevsellik olmayacak full stack tarzda bir şey arayanlar için aşağıdaki seriyi inceleyebilirsiniz:

Baştan sona Twitter'ı yazalım #1 (Flutter, Riverpod, Fpdart, Appwrite)
Baştan sona Twitter'ı yazalım #2 (Flutter, Riverpod, Fpdart, Appwrite)

Bu projede basitçe dummy data ile çalışıyoruz. Yeni bir flutter projesi açıyoruz ve main.dart içerisindeki kodların hepsini siliyoruz. Ardından main fonksiyonumuzu yazıp material.dart kütüphanesini import ediyoruz. İstediğiniz isimle bir class oluşturun ve bu class uygulamanın temel ayarlamalarını yapacak ona göre bir isim verin default olarak Flutterda MyApp olarak gelir bu isim. debug banner'ının gözükmemesi için debugShowCheckedModeBanner:false olarak ayarlayın ve yine istediğiniz bir title verin. Tema olarak da ThemeData.dark().copyWith metodunu kullanacağız yani karanlık temayı kullanacağız fakat bazı şeyleri değiştireceğiz. Son hali bu şekilde olacak:

fsO8CM.png


easter egg gibi şakalar falan :D
@hoaydar @Mirliva @Enistein @musileno @'Türk @WHITERUBY @нydrαтнαlleѕ @Reiɴα

main.dart:

Kod:
import 'package:flutter/material.dart';
import 'package:whatsapp_ui/colors.dart';
void main() {
  runApp(const MyApp());
}
class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Whatsapp UI',
      theme: ThemeData.dark().copyWith(
        scaffoldBackgroundColor: backgroundColor,  
      ), 
      home: const Text('Hello world')
    );
  }
}

ayrıca lib klasörünün içerisinde bize lazım olacak renkleri içeren colors.dart dosyamızı oluşturuyoruz.

Kod:
import 'dart:ui';

const backgroundColor = Color.fromRGBO(19, 28, 33,1);
const textColor = Color.fromRGBO(241, 241, 242, 1);
const appBarColor = Color.fromRGBO(31,44,52,1);
const webAppBarColor = Color.fromRGBO(42,47,50,1);
const messageColor = Color.fromRGBO(5, 96, 98, 1);
const senderMessageColor = Color.fromRGBO(37, 45, 49, 1);
const tabColor = Color.fromRGBO(0, 167, 131, 1);
const searchBarColor = Color.fromRGBO(50, 55, 57, 1);
const dividerColor = Color.fromRGBO(37, 45, 50, 1);
const chatBarMessage = Color.fromRGBO(30, 36, 40, 1);
const mobileChatBoxColor = Color.fromRGBO(31, 44, 52, 1);

ardından lib klasörü altında responsive adında veya istediğiniz isimle bir klasör oluşturuyoruz ve responsive_layout.dart dosyası içerisinde bir statelesswidget oluşturuyoruz.

Kod:
import 'package:flutter/material.dart';

class ResponsiveLayout extends StatelessWidget {
  const ResponsiveLayout({super.key});

  @override
  Widget build(BuildContext context) {
    return const Placeholder();
  }
}

responsive_layout.dart dosyası içerisinde constructordan mobil ekran için bir layout ve web ekranı için bir layout isteyeceğiz ve LayoutBuilder kullanarak eğer maxWidth 900'den fazlaysa web ekranını göster değilse mobil ekranı göster şeklinde bir tasarım yapacağız.

responsive_layout.dart:

Kod:
import 'package:flutter/material.dart';

class ResponsiveLayout extends StatelessWidget {
  final Widget mobileScreenLayout;
  final Widget webScreenLayout;
  const ResponsiveLayout({
    super.key,
    required this.mobileScreenLayout,
    required this.webScreenLayout,
  });

  @override
  Widget build(BuildContext context) {
    return const LayoutBuilder(
      builder: (context, constraints) {
        if (constraints.maxWidth > 900) {
          //web ekrani
        }
        //mobil ekran
      },
    );
  }
}

şimdi de lib klasörü içerisinde mobile_screen_layout.dart ve web_screen_layout.dart dosyalarını açıyoruz amaç tekrar kullanılabilir widgetlar kullanarak bu iki farklı screeni yazmak ve responsive layouta vermek.

mobile_screen_layout.dart:

Kod:
import 'package:flutter/material.dart';

class MobileScreenLayout extends StatelessWidget {
  const MobileScreenLayout({super.key});

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: Center(child: Text('mobile screen'))
    );
  }
}

web_screen_layout.dart:

Kod:
import 'package:flutter/material.dart';

class WebScreenLayout extends StatelessWidget {
  const WebScreenLayout({super.key});

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: Center(child: Text('web screen'))
    );
  }
}

ve şimdi main.dart içerisinde responsivelayout'u home kısmında kullanıyoruz

Kod:
import 'package:flutter/material.dart';
import 'package:whatsapp_ui/colors.dart';
import 'package:whatsapp_ui/responsive/responsive_layout.dart';
import 'package:whatsapp_ui/screens/mobile_screen_layout.dart';
import 'package:whatsapp_ui/screens/web_screen_layout.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Whatsapp UI',
      theme: ThemeData.dark().copyWith(
        scaffoldBackgroundColor: backgroundColor,
      ),
      home: const ResponsiveLayout(
        mobileScreenLayout: MobileScreenLayout(),
        webScreenLayout: WebScreenLayout(),
      ),
    );
  }
}

şimdi ayrı ayrı tasarımı yapmaya başlayabiliriz. mobil ve web için yani. öncelikle mobil için bir DefaultTabController widgetı ve ona child olarak Scaffold koyup appBar kısmına rengi gri olan title'ında içinde 'Whatsapp' yazan bir Text widgetı (fontSize'ı 20 olarak ve fontu kalınlaştırdık) daha sonra AppBar'ın actions özelliği bizden Widgetlar bekliyor liste şeklinde bu özellik de oraya eklediğimiz widgetlar appbar'ın sağ tarafından başlar gelmeye 2 tane IconButton ekliyoruz iconlardan birini search diğerini more_vert olarak belirliyoruz.

mobile_screen_layout.dart:

Kod:
import 'package:flutter/material.dart';

class MobileScreenLayout extends StatelessWidget {
  const MobileScreenLayout({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: DefaultTabController(
        length: 3,
        child: Scaffold(
          appBar: AppBar(
            title: const Text(
              'Whatsapp',
              style: TextStyle(
                color: Colors.grey,
                fontSize: 20,
                fontWeight: FontWeight.bold,
              ),
            ),
            actions: [
              IconButton(
                onPressed: () {},
                icon: const Icon(Icons.search),
              ),
              IconButton(
                onPressed: () {},
                icon: const Icon(Icons.more_vert),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

şimdi normalde yukarıda appbar'ın kendi bottom özelliğine TabBar verip Tab() şeklinde sohbetler, güncellemeler, topluluklar, aramalar tabbarını oluşturacaktık ancak Whatsapp son zamanlarda değişiklikler yaptığı için biz de TabBar'ı aşağıya alalım yani scaffold'un bottomNavigationBar kısmına TabBar'ımızı yazalım

mobile_screen_layout.dart:

Kod:
import 'package:flutter/material.dart';
import 'package:whatsapp_ui/colors.dart';

class MobileScreenLayout extends StatelessWidget {
  const MobileScreenLayout({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: DefaultTabController(
        length: 4,
        child: Scaffold(
          appBar: AppBar(
            title: const Text(
              'Whatsapp',
              style: TextStyle(
                color: Colors.grey,
                fontSize: 20,
                fontWeight: FontWeight.bold,
              ),
            ),
            actions: [
              IconButton(
                onPressed: () {},
                icon: const Icon(Icons.search),
              ),
              IconButton(
                onPressed: () {},
                icon: const Icon(Icons.more_vert),
              ),
            ],
          ),
          bottomNavigationBar: const TabBar(
            indicatorColor: tabColor,
            indicatorWeight: 4,
            labelColor: Colors.white,
            unselectedLabelColor: Colors.grey,
            labelStyle: TextStyle(fontWeight: FontWeight.bold),
            tabs: [
              Tab(text: 'Sohbetler', icon: Icon(Icons.message)),
              Tab(text: 'Güncellemeler', icon: Icon(Icons.all_inbox_sharp)),
              Tab(
                  text: 'Topluluklar',
                  icon: Icon(
                    Icons.people,
                  )),
              Tab(text: 'Aramalar', icon: Icon(Icons.call)),
            ],
          ),
        ),
      ),
    );
  }
}

burada bir tabbar oluşturup indicatorColor olarak tabColor'u verdik önceden ayarlamış olduğumuz colors.dart dosyasından çünkü orada whatsapp'ın renkleri var. indicatorWeight'ini biraz artırıp 4 yaptık labelColor olarak beyaz ve seçilmemiş label color olarak da gri verdik. labelları textstyle vererek kalınlaştırdık ve tableri tek tek verdik. Normalde tablere sadece text atayacaktım ancak whatsapp'ta iconlar da mevcut o nedenle tablerin icon özelliğini boş bırakmayıp alakalı iconları verdim. Daha iyi iconlar ya da tam olarak aynı iconu bulup ekleyebilirsiniz ben rastgele verdim biraz.

backgroundColor: appBarColor,
elevation: 0, olarak eklemeyi de unutmayın appBar'a.

şimdi dummy data gerekiyor tasarımı yapmamız ve dolu gözükmesi açısından.

lib klasörü içerisine tıpkı colors.dart'da yaptığımız gibi info.dart:

Kod:
const info = [

  {

    'name': 'Mirliva',

    'message': 'nasıl gidiyor?',

    'time': '3:53 pm',

    'profilePic':

        'https://www.turkhackteam.org/data/avatars/l/785/785382.jpg?1715039299',

  },

  {

    'name': 'Türk',

    'message': 'he de geç',

    'time': '2:25 pm',

    'profilePic':

        'https://www.turkhackteam.org/data/avatars/l/908/908744.jpg?1714607047',

  },

  {

    'name': 'Enistein',

    'message': 'krav maga biliyorum hoaydarı teklerim',

    'time': '1:03 pm',

    'profilePic':

        'https://www.turkhackteam.org/data/avatars/l/664/664840.jpg?1675139284',

  },

  {

    'name': 'Reina',

    'message': 'ahahaha',

    'time': '12:06 pm',

    'profilePic':

        'https://www.turkhackteam.org/data/avatars/l/879/879110.jpg?1661379848',

  }
];

ardından contacts_list.dart diye bir dosya oluşturalım veya siz nasıl adlandırmak isterseniz bu kişiler listesi olacak ana kısımdaki. bunu lib klasörü altında widgets klasörü açıyoruz tekrar tekrar kullanılan bir widget olacağı için.

Kod:
import 'package:flutter/material.dart';
import 'package:whatsapp_ui/info.dart';

class ContactsList extends StatelessWidget {
  const ContactsList({super.key});

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.only(top: 10),
      child: ListView.builder(
        shrinkWrap: true,
        itemCount: info.length,
        itemBuilder: (context, index) {
          var user = info[index];
          return InkWell(
            onTap: () {
              
            },
            child: Padding(
              padding: const EdgeInsets.only(bottom: 8),
              child: ListTile(
                title: Text(
                  user['name'].toString(),
                  style: const TextStyle(fontSize: 15),
                ),
                subtitle: Padding(
                  padding: const EdgeInsets.only(top: 6),
                  child: Text(
                    user['message'].toString(),
                    style: const TextStyle(fontSize: 15, color: Colors.grey),
                  ),
                ),
                leading: CircleAvatar(
                  backgroundImage: NetworkImage(
                    user['profilePic'].toString(),
                  ),
                ),
                trailing: Text(
                  user['time'].toString(),
                  style: const TextStyle(fontSize: 13, color: Colors.grey),
                ),
              ),
            ),
          );
        },
      ),
    );
  }
}

öncelikle material'ı import edip stless bir widget oluşturuyoruz ardından sadece üstten padding verdiğimiz bir ListView.builder döndürmesini istiyoruz. shrinkWrap özelliğini true olarak ayarladıktan sonra itemCount olarak info.dart dosyamızdaki listenin uzunluğu kadar olmasını istiyoruz yani info.length ardından itemBuilder kısmında o anki indexi user'a atıyoruz yani ilk nesne ikinci nesne üçüncü nesne... şeklinde gösterileceği için o ankini yakalıyoruz ve InkWell döndürüyoruz tıklanabilir olması için. Daha sonra bu InkWell widgetı içerisinde bir ListTile döndürüyoruz. ListTile widgetı bizim işimize çok yarayacak çünkü yapmak istediğimiz her şeyi karşılıyor. title kısmına info listesindeki ilgili nesnenin name'ini ardından benzer şekilde subtitle kısmında mesajı ve benzer şekilde CircleAvatar oluşturup NetworkImage kullanarak profilePic'i ve en son da zamanı sağda göstermesi için trailing kullanarak Text widgetı ile ilgili nesnenin time'ını gösteriyoruz.

fsOl7h.png


Şimdi devam edelim web_screen_layout.dart kısmını yazmaya başlayalım

Kod:
import 'package:flutter/material.dart';
import 'package:whatsapp_ui/widgets/contacts_list.dart';

class WebScreenLayout extends StatelessWidget {
  const WebScreenLayout({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Row(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          Column(
            children: [
              //web profil barı
              //web search barı
              const ContactsList(),
            ],
          ),
          //web ekranı
        ],
      )
    );
  }
}

temel yapı bu şekilde buradan yazmaya devam edelim assets klasörü oluşturalım bir de projede ve oraya da whatsapp'ın klasik background resmini atalım. Her yerden bulabilirsiniz

fsO3pK.png


web_screen_layout.dart'a şu eklemeleri yaptık:

Kod:
import 'package:flutter/material.dart';
import 'package:whatsapp_ui/widgets/contacts_list.dart';

class WebScreenLayout extends StatelessWidget {
  const WebScreenLayout({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Row(
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: [
        Expanded(
          child: SingleChildScrollView(
            child: Column(
              children: [
                //web profil barı
                //web search barı
                const ContactsList(),
              ],
            ),
          ),
        ),
        Container(
          width: MediaQuery.of(context).size.width*0.7,
          decoration: BoxDecoration(
            image: DecorationImage(
              image: AssetImage('assets/backgroundImage.png'),
              fit: BoxFit.cover,
            ),
          ),
        ),
      ],
    ));
  }
}

columnı singlechildscrollview ile sarmaladıktan sonra onu da expanded ile sarmaladık ve background için assets/backgroundImage.png mizi verdik assets klasörü açıp whatsapp'ın klasik chat backgroundunu verdik yani. onu da container ile sarmaladık ve width olarak size'ın 0.7 ile çarparak alan ayırdık. şimdi kalan kısımları bitirmeye başlayalım.

burada web profil barı için yeni bir widget açmak yerine direkt orada yazalım kontrol etmek için flutter run -d chrome yazıp çalıştırabilirsiniz tarayıcıda oradan kontrol edebilirsiniz.

fsOB1Q.png


Kod:
import 'package:flutter/material.dart';
import 'package:whatsapp_ui/colors.dart';
import 'package:whatsapp_ui/widgets/contacts_list.dart';

class WebScreenLayout extends StatelessWidget {
  const WebScreenLayout({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Row(
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: [
        Expanded(
          child: SingleChildScrollView(
            child: Column(
              children: [
                AppBar(
                  backgroundColor: backgroundColor,
                  title: const Text(
                    'Chats',
                    style: TextStyle(
                      color: Colors.grey,
                      fontSize: 20,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  actions: const [
                    Padding(
                      padding: EdgeInsets.all(8.0),
                      child: Icon(
                        
                        Icons.chat,
                        color: Colors.grey,
                      ),
                    ),
                    Padding(
                      padding: EdgeInsets.all(8.0),
                      child: Icon(
                        Icons.more_vert,
                        color: Colors.grey,
                      ),
                    ),
                  ],
                ),
                const ContactsList(),
              ],
            ),
          ),
        ),
        Container(
          width: MediaQuery.of(context).size.width * 0.7,
          decoration: const BoxDecoration(
            image: DecorationImage(
              image: AssetImage('assets/backgroundImage.png'),
              fit: BoxFit.cover,
            ),
          ),
        ),
      ],
    ));
  }
}

web tarafında search bar kısmı ve yanda iki küçük icon var onları yapmayacağım isterseniz ekleyebilirsiniz. sağdaki chat kısmı için widgets klasörüne yeni bir widget oluşturalım ve oraya yazıp sonra kullanalım.

şöyle değişiklikler yaptım

widgets> web_chat_appbar.dart

Kod:
import 'package:flutter/material.dart';
import 'package:whatsapp_ui/colors.dart';

class WebChatAppBar extends StatelessWidget {
  final Map<String, String> user;
  const WebChatAppBar({super.key, required this.user});

  @override
  Widget build(BuildContext context) {
    return Container(
      color: backgroundColor,
      child: ListTile(
        
        title: Text(
          user['name'].toString(),
          style: const TextStyle(fontSize: 15),
        ),
        leading: Padding(
          padding: const EdgeInsets.all(8.0),
          child: CircleAvatar(
            radius: 30,
            backgroundImage: NetworkImage(
              user['profilePic'].toString(),
            ),
          ),
        ),
      ),
    );
  }
}

ve web_screen_layout.dart

Kod:
import 'package:flutter/material.dart';
import 'package:whatsapp_ui/colors.dart';
import 'package:whatsapp_ui/info.dart';
import 'package:whatsapp_ui/widgets/contacts_list.dart';
import 'package:whatsapp_ui/widgets/web_chat_appbar.dart';

class WebScreenLayout extends StatefulWidget {
  const WebScreenLayout({super.key});

  @override
  State<WebScreenLayout> createState() => _WebScreenLayoutState();
}

class _WebScreenLayoutState extends State<WebScreenLayout> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Row(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          Expanded(
            child: SingleChildScrollView(
              child: Column(
                children: [
                  AppBar(
                    backgroundColor: backgroundColor,
                    title: const Text(
                      'Chats',
                      style: TextStyle(
                        color: Colors.grey,
                        fontSize: 20,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                    actions: const [
                      Padding(
                        padding: EdgeInsets.all(8.0),
                        child: Icon(
                          Icons.chat,
                          color: Colors.grey,
                        ),
                      ),
                      Padding(
                        padding: EdgeInsets.all(8.0),
                        child: Icon(
                          Icons.more_vert,
                          color: Colors.grey,
                        ),
                      ),
                    ],
                  ),
                  const ContactsList(),
                ],
              ),
            ),
          ),
          Container(
            width: MediaQuery.of(context).size.width * 0.7,
            decoration: const BoxDecoration(
              image: DecorationImage(
                image: AssetImage('assets/backgroundImage.png'),
                fit: BoxFit.cover,
              ),
            ),
            child: const Column(
              children: [
                WebChatAppBar(user: first),
                //msjlar
                //msj kutusu
              ],
            ),
          ),
        ],
      ),
    );
  }
}

şimdi chat kısmını halletmemiz gerekiyor. web tarafı için sağda gözükecek dummy şekilde sadece görüntü olacak herhangi bir işlevselliğe girmeden

chat_list.dart :

Kod:
import 'package:flutter/material.dart';
import 'package:whatsapp_ui/info.dart';
import 'package:whatsapp_ui/widgets/my_message_card.dart';
import 'package:whatsapp_ui/widgets/sender_message_card.dart';


class ChatList extends StatelessWidget {
  const ChatList({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Expanded(
      child: ListView.builder(
        itemCount: messages.length,
        itemBuilder: (context, index) {
          if (messages[index]['isMe'] == true) {
            return MyMessageCard(
              message: messages[index]['text'].toString(),
              date: messages[index]['time'].toString(),
            );
          }
          return SenderMessageCard(
            message: messages[index]['text'].toString(),
            date: messages[index]['time'].toString(),
          );
        },
      ),
    );
  }
}

chatlist diye bir stless widget oluşturduk. daha sonra listview.builder döndürüyoruz expanded ile sarmaladık ardından itemCount olarak info.dart içerisinde size şimdi vereceğim komik mesajları çekiyoruz ve gösteriyoruz. isMe eğer true ise mesajı biz eğer false ise karşısı atıyormuş gibi gösterecek bir mantık var. bizim mesajımız ise mymessagecard döndürülecek karşısının mesajı ise sendermessagecard gösterilecek. O ikisini de ayrı dosyada kodluyoruz :

Kod:
import 'package:flutter/material.dart';
import 'package:whatsapp_ui/colors.dart';

class MyMessageCard extends StatelessWidget {
  final String message;
  final String date;

  const MyMessageCard({Key? key, required this.message, required this.date}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Align(
      alignment: Alignment.centerRight,
      child: ConstrainedBox(
        constraints: BoxConstraints(
          maxWidth: MediaQuery.of(context).size.width - 45,
        ),
        child: Card(
          elevation: 1,
          shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
          color: messageColor,
          margin: const EdgeInsets.symmetric(horizontal: 15, vertical: 5),
          child: Stack(
            children: [
              Padding(
                padding: const EdgeInsets.only(
                  left: 10,
                  right: 30,
                  top: 5,
                  bottom: 20,
                ),
                child: Text(
                  message,
                  style: const TextStyle(
                    fontSize: 16,
                  ),
                ),
              ),
              Positioned(
                bottom: 4,
                right: 10,
                child: Row(
                  children: [
                    Text(
                      date,
                      style:const TextStyle(
                        fontSize: 13,
                        color: Colors.white60,
                      ),
                    ),
                    const SizedBox(
                      width: 5,
                    ),
                    const Icon(
                      Icons.done_all,
                      size: 20,
                      color: Colors.white60,
                    ),
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

yukarısı MyMessageCard bu widget 2 tane şey bekleyecek biri mesajlar diğeri de tarih. Daha sonra Card döndürüyoruz bir hizalama yaptıktan sonra bu card widgetında biraz ayarlamalar yapıyoruz elevation, shape gibi ardından Stack widgetını döndürüyor bu card çünkü üst üste kullanacağımız bir yapı var. Yan yana olacağı için Row içerisinde Text ve SizedBox ile küçük bir alan verip Icon'u da ekliyoruz. Ayarlamaları yaparken MediaQuery.of(context) kullanarak sizeı çekebiliyoruz düzgün şekilde. sender_message_card.dart dosyası da benzer kodlar fakat hizalamayı centerLeft yapıyoruz.

Kod:
import 'package:flutter/material.dart';
import 'package:whatsapp_ui/colors.dart';

class SenderMessageCard extends StatelessWidget {
  const SenderMessageCard({
    Key? key,
    required this.message,
    required this.date,
  }) : super(key: key);
  final String message;
  final String date;

  @override
  Widget build(BuildContext context) {
    return Align(
      alignment: Alignment.centerLeft,
      child: ConstrainedBox(
        constraints: BoxConstraints(
          maxWidth: MediaQuery.of(context).size.width - 45,
        ),
        child: Card(
          elevation: 1,
          shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
          color: senderMessageColor,
          margin: const EdgeInsets.symmetric(horizontal: 15, vertical: 5),
          child: Stack(
            children: [
              Padding(
                padding: const EdgeInsets.only(
                  left: 10,
                  right: 30,
                  top: 5,
                  bottom: 20,
                ),
                child: Text(
                  message,
                  style: const TextStyle(
                    fontSize: 16,
                  ),
                ),
              ),
              Positioned(
                bottom: 2,
                right: 10,
                child: Text(
                  date,
                  style: TextStyle(
                    fontSize: 13,
                    color: Colors.grey[600],
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Sonuç olarak responsive bir tasarım elde ediyoruz. info.dart taki dummy için tekrardan inceleyin:

Kod:
const info = [
  {
    'name': 'Mirliva',
    'message': '200 ile gidiyordu ama kurtulamadı elimden',
    'time': '3:53 pm',
    'profilePic': 'https://www.turkhackteam.org/data/avatars/l/785/785382.jpg?1715039299',
  },
  {
    'name': 'Türk',
    'message': '35 yaşında gösteriyorsun',
    'time': '2:25 pm',
    'profilePic': 'https://www.turkhackteam.org/data/avatars/l/908/908744.jpg?1714607047',
  },
  {
    'name': 'Enistein',
    'message': 'krav maga biliyorum hoaydarı teklerim',
    'time': '1:03 pm',
    'profilePic': 'https://www.turkhackteam.org/data/avatars/l/664/664840.jpg?1675139284',
  },
  {
    'name': 'Reina',
    'message': 'ahahaha',
    'time': '12:06 pm',
    'profilePic': 'https://www.turkhackteam.org/data/avatars/l/879/879110.jpg?1661379848',
  },
  {
    'name': 'WHITERUBY',
    'message': 'ondan sonra işte yeter artık lan bu eğitim dedim',
    'time': '12:06 pm',
    'profilePic': 'https://www.turkhackteam.org/data/avatars/m/997/997470.jpg?1714059680',
  },
  {
    'name': 'musileno',
    'message': 'ne yaptın usta nasılsın iyi misin :)',
    'time': '2007',
    'profilePic': 'https://resmim.net/cdn/2024/05/07/fsejCR.jpg',
  },
  {
    'name': 'HydraThalles',
    'message': 'Omen ile yine kanserledim',
    'time': 'Premier',
    'profilePic': 'https://www.turkhackteam.org/data/avatars/l/806/806676.jpg?1652131820',
  }
];

const first = {
  'name': 'Enistein',
  'message': 'krav maga biliyorum hoaydarı teklerim',
  'time': '1:03 pm',
  'profilePic': 'https://www.turkhackteam.org/data/avatars/l/664/664840.jpg?1675139284',
};

const messages = [
  {"isMe": false, "text": "dediğim gibi işte ondan sonra google'ı hackledim", "time": "10:00 am"},
  {"isMe": true, "text": "oha nasıl ya", "time": "11:00 am"},
  {"isMe": false, "text": "hoaydar napıyor gelsin şiir okusun", "time": "11:01 am"},
  {"isMe": true, "text": "bilmiyorum da geçen seni alırım dediydi", "time": "11:01 am"},
  {"isMe": false, "text": "yaklaşamaz krav maga var bende", "time": "11:03 am"},
  {"isMe": true, "text": "belli olmaz ya", "time": "11:04 am"},
  {"isMe": false, "text": "krav maga biliyorum hoaydarı teklerim direkt", "time": "11:05 am"},
 
];

fsOT1C.gif


benzer şekilde chat screen'i içerisinde hem web için hem mobil için text input alanı için widgetlar yazıp tamamlayabilirsiniz. Bir sonraki konuda görüşmek üzere

<3 Gauloran



 

RW

'Türk ün yaveri
26 Kas 2020
2,068
19
1,520
Konum
Elinize emeğinize sağlık hocam yapyorsunuz bu sporu 🫡
 

Taha'

Moderatör
30 Kas 2018
609
10
677
Merhaba bu konuda Flutter ile whatsapp ui yazmaya çalışacağız hem mobil hem de web için responsive bir tasarım oluşturmayı hedefliyoruz işlevsellik olmayacak full stack tarzda bir şey arayanlar için aşağıdaki seriyi inceleyebilirsiniz:

Baştan sona Twitter'ı yazalım #1 (Flutter, Riverpod, Fpdart, Appwrite)
Baştan sona Twitter'ı yazalım #2 (Flutter, Riverpod, Fpdart, Appwrite)

Bu projede basitçe dummy data ile çalışıyoruz. Yeni bir flutter projesi açıyoruz ve main.dart içerisindeki kodların hepsini siliyoruz. Ardından main fonksiyonumuzu yazıp material.dart kütüphanesini import ediyoruz. İstediğiniz isimle bir class oluşturun ve bu class uygulamanın temel ayarlamalarını yapacak ona göre bir isim verin default olarak Flutterda MyApp olarak gelir bu isim. debug banner'ının gözükmemesi için debugShowCheckedModeBanner:false olarak ayarlayın ve yine istediğiniz bir title verin. Tema olarak da ThemeData.dark().copyWith metodunu kullanacağız yani karanlık temayı kullanacağız fakat bazı şeyleri değiştireceğiz. Son hali bu şekilde olacak:

fsO8CM.png


easter egg gibi şakalar falan :D
@hoaydar @Mirliva @Enistein @musileno @'Türk @WHITERUBY @нydrαтнαlleѕ @Reiɴα

main.dart:

Kod:
import 'package:flutter/material.dart';
import 'package:whatsapp_ui/colors.dart';
void main() {
  runApp(const MyApp());
}
class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Whatsapp UI',
      theme: ThemeData.dark().copyWith(
        scaffoldBackgroundColor: backgroundColor, 
      ),
      home: const Text('Hello world')
    );
  }
}

ayrıca lib klasörünün içerisinde bize lazım olacak renkleri içeren colors.dart dosyamızı oluşturuyoruz.

Kod:
import 'dart:ui';

const backgroundColor = Color.fromRGBO(19, 28, 33,1);
const textColor = Color.fromRGBO(241, 241, 242, 1);
const appBarColor = Color.fromRGBO(31,44,52,1);
const webAppBarColor = Color.fromRGBO(42,47,50,1);
const messageColor = Color.fromRGBO(5, 96, 98, 1);
const senderMessageColor = Color.fromRGBO(37, 45, 49, 1);
const tabColor = Color.fromRGBO(0, 167, 131, 1);
const searchBarColor = Color.fromRGBO(50, 55, 57, 1);
const dividerColor = Color.fromRGBO(37, 45, 50, 1);
const chatBarMessage = Color.fromRGBO(30, 36, 40, 1);
const mobileChatBoxColor = Color.fromRGBO(31, 44, 52, 1);

ardından lib klasörü altında responsive adında veya istediğiniz isimle bir klasör oluşturuyoruz ve responsive_layout.dart dosyası içerisinde bir statelesswidget oluşturuyoruz.

Kod:
import 'package:flutter/material.dart';

class ResponsiveLayout extends StatelessWidget {
  const ResponsiveLayout({super.key});

  @override
  Widget build(BuildContext context) {
    return const Placeholder();
  }
}

responsive_layout.dart dosyası içerisinde constructordan mobil ekran için bir layout ve web ekranı için bir layout isteyeceğiz ve LayoutBuilder kullanarak eğer maxWidth 900'den fazlaysa web ekranını göster değilse mobil ekranı göster şeklinde bir tasarım yapacağız.

responsive_layout.dart:

Kod:
import 'package:flutter/material.dart';

class ResponsiveLayout extends StatelessWidget {
  final Widget mobileScreenLayout;
  final Widget webScreenLayout;
  const ResponsiveLayout({
    super.key,
    required this.mobileScreenLayout,
    required this.webScreenLayout,
  });

  @override
  Widget build(BuildContext context) {
    return const LayoutBuilder(
      builder: (context, constraints) {
        if (constraints.maxWidth > 900) {
          //web ekrani
        }
        //mobil ekran
      },
    );
  }
}

şimdi de lib klasörü içerisinde mobile_screen_layout.dart ve web_screen_layout.dart dosyalarını açıyoruz amaç tekrar kullanılabilir widgetlar kullanarak bu iki farklı screeni yazmak ve responsive layouta vermek.

mobile_screen_layout.dart:

Kod:
import 'package:flutter/material.dart';

class MobileScreenLayout extends StatelessWidget {
  const MobileScreenLayout({super.key});

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: Center(child: Text('mobile screen'))
    );
  }
}

web_screen_layout.dart:

Kod:
import 'package:flutter/material.dart';

class WebScreenLayout extends StatelessWidget {
  const WebScreenLayout({super.key});

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: Center(child: Text('web screen'))
    );
  }
}

ve şimdi main.dart içerisinde responsivelayout'u home kısmında kullanıyoruz

Kod:
import 'package:flutter/material.dart';
import 'package:whatsapp_ui/colors.dart';
import 'package:whatsapp_ui/responsive/responsive_layout.dart';
import 'package:whatsapp_ui/screens/mobile_screen_layout.dart';
import 'package:whatsapp_ui/screens/web_screen_layout.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Whatsapp UI',
      theme: ThemeData.dark().copyWith(
        scaffoldBackgroundColor: backgroundColor,
      ),
      home: const ResponsiveLayout(
        mobileScreenLayout: MobileScreenLayout(),
        webScreenLayout: WebScreenLayout(),
      ),
    );
  }
}

şimdi ayrı ayrı tasarımı yapmaya başlayabiliriz. mobil ve web için yani. öncelikle mobil için bir DefaultTabController widgetı ve ona child olarak Scaffold koyup appBar kısmına rengi gri olan title'ında içinde 'Whatsapp' yazan bir Text widgetı (fontSize'ı 20 olarak ve fontu kalınlaştırdık) daha sonra AppBar'ın actions özelliği bizden Widgetlar bekliyor liste şeklinde bu özellik de oraya eklediğimiz widgetlar appbar'ın sağ tarafından başlar gelmeye 2 tane IconButton ekliyoruz iconlardan birini search diğerini more_vert olarak belirliyoruz.

mobile_screen_layout.dart:

Kod:
import 'package:flutter/material.dart';

class MobileScreenLayout extends StatelessWidget {
  const MobileScreenLayout({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: DefaultTabController(
        length: 3,
        child: Scaffold(
          appBar: AppBar(
            title: const Text(
              'Whatsapp',
              style: TextStyle(
                color: Colors.grey,
                fontSize: 20,
                fontWeight: FontWeight.bold,
              ),
            ),
            actions: [
              IconButton(
                onPressed: () {},
                icon: const Icon(Icons.search),
              ),
              IconButton(
                onPressed: () {},
                icon: const Icon(Icons.more_vert),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

şimdi normalde yukarıda appbar'ın kendi bottom özelliğine TabBar verip Tab() şeklinde sohbetler, güncellemeler, topluluklar, aramalar tabbarını oluşturacaktık ancak Whatsapp son zamanlarda değişiklikler yaptığı için biz de TabBar'ı aşağıya alalım yani scaffold'un bottomNavigationBar kısmına TabBar'ımızı yazalım

mobile_screen_layout.dart:

Kod:
import 'package:flutter/material.dart';
import 'package:whatsapp_ui/colors.dart';

class MobileScreenLayout extends StatelessWidget {
  const MobileScreenLayout({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: DefaultTabController(
        length: 4,
        child: Scaffold(
          appBar: AppBar(
            title: const Text(
              'Whatsapp',
              style: TextStyle(
                color: Colors.grey,
                fontSize: 20,
                fontWeight: FontWeight.bold,
              ),
            ),
            actions: [
              IconButton(
                onPressed: () {},
                icon: const Icon(Icons.search),
              ),
              IconButton(
                onPressed: () {},
                icon: const Icon(Icons.more_vert),
              ),
            ],
          ),
          bottomNavigationBar: const TabBar(
            indicatorColor: tabColor,
            indicatorWeight: 4,
            labelColor: Colors.white,
            unselectedLabelColor: Colors.grey,
            labelStyle: TextStyle(fontWeight: FontWeight.bold),
            tabs: [
              Tab(text: 'Sohbetler', icon: Icon(Icons.message)),
              Tab(text: 'Güncellemeler', icon: Icon(Icons.all_inbox_sharp)),
              Tab(
                  text: 'Topluluklar',
                  icon: Icon(
                    Icons.people,
                  )),
              Tab(text: 'Aramalar', icon: Icon(Icons.call)),
            ],
          ),
        ),
      ),
    );
  }
}

burada bir tabbar oluşturup indicatorColor olarak tabColor'u verdik önceden ayarlamış olduğumuz colors.dart dosyasından çünkü orada whatsapp'ın renkleri var. indicatorWeight'ini biraz artırıp 4 yaptık labelColor olarak beyaz ve seçilmemiş label color olarak da gri verdik. labelları textstyle vererek kalınlaştırdık ve tableri tek tek verdik. Normalde tablere sadece text atayacaktım ancak whatsapp'ta iconlar da mevcut o nedenle tablerin icon özelliğini boş bırakmayıp alakalı iconları verdim. Daha iyi iconlar ya da tam olarak aynı iconu bulup ekleyebilirsiniz ben rastgele verdim biraz.

backgroundColor: appBarColor,
elevation: 0, olarak eklemeyi de unutmayın appBar'a.

şimdi dummy data gerekiyor tasarımı yapmamız ve dolu gözükmesi açısından.

lib klasörü içerisine tıpkı colors.dart'da yaptığımız gibi info.dart:

Kod:
const info = [

  {

    'name': 'Mirliva',

    'message': 'nasıl gidiyor?',

    'time': '3:53 pm',

    'profilePic':

        'https://www.turkhackteam.org/data/avatars/l/785/785382.jpg?1715039299',

  },

  {

    'name': 'Türk',

    'message': 'he de geç',

    'time': '2:25 pm',

    'profilePic':

        'https://www.turkhackteam.org/data/avatars/l/908/908744.jpg?1714607047',

  },

  {

    'name': 'Enistein',

    'message': 'krav maga biliyorum hoaydarı teklerim',

    'time': '1:03 pm',

    'profilePic':

        'https://www.turkhackteam.org/data/avatars/l/664/664840.jpg?1675139284',

  },

  {

    'name': 'Reina',

    'message': 'ahahaha',

    'time': '12:06 pm',

    'profilePic':

        'https://www.turkhackteam.org/data/avatars/l/879/879110.jpg?1661379848',

  }
];

ardından contacts_list.dart diye bir dosya oluşturalım veya siz nasıl adlandırmak isterseniz bu kişiler listesi olacak ana kısımdaki. bunu lib klasörü altında widgets klasörü açıyoruz tekrar tekrar kullanılan bir widget olacağı için.

Kod:
import 'package:flutter/material.dart';
import 'package:whatsapp_ui/info.dart';

class ContactsList extends StatelessWidget {
  const ContactsList({super.key});

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.only(top: 10),
      child: ListView.builder(
        shrinkWrap: true,
        itemCount: info.length,
        itemBuilder: (context, index) {
          var user = info[index];
          return InkWell(
            onTap: () {
             
            },
            child: Padding(
              padding: const EdgeInsets.only(bottom: 8),
              child: ListTile(
                title: Text(
                  user['name'].toString(),
                  style: const TextStyle(fontSize: 15),
                ),
                subtitle: Padding(
                  padding: const EdgeInsets.only(top: 6),
                  child: Text(
                    user['message'].toString(),
                    style: const TextStyle(fontSize: 15, color: Colors.grey),
                  ),
                ),
                leading: CircleAvatar(
                  backgroundImage: NetworkImage(
                    user['profilePic'].toString(),
                  ),
                ),
                trailing: Text(
                  user['time'].toString(),
                  style: const TextStyle(fontSize: 13, color: Colors.grey),
                ),
              ),
            ),
          );
        },
      ),
    );
  }
}

öncelikle material'ı import edip stless bir widget oluşturuyoruz ardından sadece üstten padding verdiğimiz bir ListView.builder döndürmesini istiyoruz. shrinkWrap özelliğini true olarak ayarladıktan sonra itemCount olarak info.dart dosyamızdaki listenin uzunluğu kadar olmasını istiyoruz yani info.length ardından itemBuilder kısmında o anki indexi user'a atıyoruz yani ilk nesne ikinci nesne üçüncü nesne... şeklinde gösterileceği için o ankini yakalıyoruz ve InkWell döndürüyoruz tıklanabilir olması için. Daha sonra bu InkWell widgetı içerisinde bir ListTile döndürüyoruz. ListTile widgetı bizim işimize çok yarayacak çünkü yapmak istediğimiz her şeyi karşılıyor. title kısmına info listesindeki ilgili nesnenin name'ini ardından benzer şekilde subtitle kısmında mesajı ve benzer şekilde CircleAvatar oluşturup NetworkImage kullanarak profilePic'i ve en son da zamanı sağda göstermesi için trailing kullanarak Text widgetı ile ilgili nesnenin time'ını gösteriyoruz.

fsOl7h.png


Şimdi devam edelim web_screen_layout.dart kısmını yazmaya başlayalım

Kod:
import 'package:flutter/material.dart';
import 'package:whatsapp_ui/widgets/contacts_list.dart';

class WebScreenLayout extends StatelessWidget {
  const WebScreenLayout({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Row(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          Column(
            children: [
              //web profil barı
              //web search barı
              const ContactsList(),
            ],
          ),
          //web ekranı
        ],
      )
    );
  }
}

temel yapı bu şekilde buradan yazmaya devam edelim assets klasörü oluşturalım bir de projede ve oraya da whatsapp'ın klasik background resmini atalım. Her yerden bulabilirsiniz

fsO3pK.png


web_screen_layout.dart'a şu eklemeleri yaptık:

Kod:
import 'package:flutter/material.dart';
import 'package:whatsapp_ui/widgets/contacts_list.dart';

class WebScreenLayout extends StatelessWidget {
  const WebScreenLayout({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Row(
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: [
        Expanded(
          child: SingleChildScrollView(
            child: Column(
              children: [
                //web profil barı
                //web search barı
                const ContactsList(),
              ],
            ),
          ),
        ),
        Container(
          width: MediaQuery.of(context).size.width*0.7,
          decoration: BoxDecoration(
            image: DecorationImage(
              image: AssetImage('assets/backgroundImage.png'),
              fit: BoxFit.cover,
            ),
          ),
        ),
      ],
    ));
  }
}

columnı singlechildscrollview ile sarmaladıktan sonra onu da expanded ile sarmaladık ve background için assets/backgroundImage.png mizi verdik assets klasörü açıp whatsapp'ın klasik chat backgroundunu verdik yani. onu da container ile sarmaladık ve width olarak size'ın 0.7 ile çarparak alan ayırdık. şimdi kalan kısımları bitirmeye başlayalım.

burada web profil barı için yeni bir widget açmak yerine direkt orada yazalım kontrol etmek için flutter run -d chrome yazıp çalıştırabilirsiniz tarayıcıda oradan kontrol edebilirsiniz.

fsOB1Q.png


Kod:
import 'package:flutter/material.dart';
import 'package:whatsapp_ui/colors.dart';
import 'package:whatsapp_ui/widgets/contacts_list.dart';

class WebScreenLayout extends StatelessWidget {
  const WebScreenLayout({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Row(
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: [
        Expanded(
          child: SingleChildScrollView(
            child: Column(
              children: [
                AppBar(
                  backgroundColor: backgroundColor,
                  title: const Text(
                    'Chats',
                    style: TextStyle(
                      color: Colors.grey,
                      fontSize: 20,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  actions: const [
                    Padding(
                      padding: EdgeInsets.all(8.0),
                      child: Icon(
                       
                        Icons.chat,
                        color: Colors.grey,
                      ),
                    ),
                    Padding(
                      padding: EdgeInsets.all(8.0),
                      child: Icon(
                        Icons.more_vert,
                        color: Colors.grey,
                      ),
                    ),
                  ],
                ),
                const ContactsList(),
              ],
            ),
          ),
        ),
        Container(
          width: MediaQuery.of(context).size.width * 0.7,
          decoration: const BoxDecoration(
            image: DecorationImage(
              image: AssetImage('assets/backgroundImage.png'),
              fit: BoxFit.cover,
            ),
          ),
        ),
      ],
    ));
  }
}

web tarafında search bar kısmı ve yanda iki küçük icon var onları yapmayacağım isterseniz ekleyebilirsiniz. sağdaki chat kısmı için widgets klasörüne yeni bir widget oluşturalım ve oraya yazıp sonra kullanalım.

şöyle değişiklikler yaptım

widgets> web_chat_appbar.dart

Kod:
import 'package:flutter/material.dart';
import 'package:whatsapp_ui/colors.dart';

class WebChatAppBar extends StatelessWidget {
  final Map<String, String> user;
  const WebChatAppBar({super.key, required this.user});

  @override
  Widget build(BuildContext context) {
    return Container(
      color: backgroundColor,
      child: ListTile(
       
        title: Text(
          user['name'].toString(),
          style: const TextStyle(fontSize: 15),
        ),
        leading: Padding(
          padding: const EdgeInsets.all(8.0),
          child: CircleAvatar(
            radius: 30,
            backgroundImage: NetworkImage(
              user['profilePic'].toString(),
            ),
          ),
        ),
      ),
    );
  }
}

ve web_screen_layout.dart

Kod:
import 'package:flutter/material.dart';
import 'package:whatsapp_ui/colors.dart';
import 'package:whatsapp_ui/info.dart';
import 'package:whatsapp_ui/widgets/contacts_list.dart';
import 'package:whatsapp_ui/widgets/web_chat_appbar.dart';

class WebScreenLayout extends StatefulWidget {
  const WebScreenLayout({super.key});

  @override
  State<WebScreenLayout> createState() => _WebScreenLayoutState();
}

class _WebScreenLayoutState extends State<WebScreenLayout> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Row(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          Expanded(
            child: SingleChildScrollView(
              child: Column(
                children: [
                  AppBar(
                    backgroundColor: backgroundColor,
                    title: const Text(
                      'Chats',
                      style: TextStyle(
                        color: Colors.grey,
                        fontSize: 20,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                    actions: const [
                      Padding(
                        padding: EdgeInsets.all(8.0),
                        child: Icon(
                          Icons.chat,
                          color: Colors.grey,
                        ),
                      ),
                      Padding(
                        padding: EdgeInsets.all(8.0),
                        child: Icon(
                          Icons.more_vert,
                          color: Colors.grey,
                        ),
                      ),
                    ],
                  ),
                  const ContactsList(),
                ],
              ),
            ),
          ),
          Container(
            width: MediaQuery.of(context).size.width * 0.7,
            decoration: const BoxDecoration(
              image: DecorationImage(
                image: AssetImage('assets/backgroundImage.png'),
                fit: BoxFit.cover,
              ),
            ),
            child: const Column(
              children: [
                WebChatAppBar(user: first),
                //msjlar
                //msj kutusu
              ],
            ),
          ),
        ],
      ),
    );
  }
}

şimdi chat kısmını halletmemiz gerekiyor. web tarafı için sağda gözükecek dummy şekilde sadece görüntü olacak herhangi bir işlevselliğe girmeden

chat_list.dart :

Kod:
import 'package:flutter/material.dart';
import 'package:whatsapp_ui/info.dart';
import 'package:whatsapp_ui/widgets/my_message_card.dart';
import 'package:whatsapp_ui/widgets/sender_message_card.dart';


class ChatList extends StatelessWidget {
  const ChatList({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Expanded(
      child: ListView.builder(
        itemCount: messages.length,
        itemBuilder: (context, index) {
          if (messages[index]['isMe'] == true) {
            return MyMessageCard(
              message: messages[index]['text'].toString(),
              date: messages[index]['time'].toString(),
            );
          }
          return SenderMessageCard(
            message: messages[index]['text'].toString(),
            date: messages[index]['time'].toString(),
          );
        },
      ),
    );
  }
}

chatlist diye bir stless widget oluşturduk. daha sonra listview.builder döndürüyoruz expanded ile sarmaladık ardından itemCount olarak info.dart içerisinde size şimdi vereceğim komik mesajları çekiyoruz ve gösteriyoruz. isMe eğer true ise mesajı biz eğer false ise karşısı atıyormuş gibi gösterecek bir mantık var. bizim mesajımız ise mymessagecard döndürülecek karşısının mesajı ise sendermessagecard gösterilecek. O ikisini de ayrı dosyada kodluyoruz :

Kod:
import 'package:flutter/material.dart';
import 'package:whatsapp_ui/colors.dart';

class MyMessageCard extends StatelessWidget {
  final String message;
  final String date;

  const MyMessageCard({Key? key, required this.message, required this.date}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Align(
      alignment: Alignment.centerRight,
      child: ConstrainedBox(
        constraints: BoxConstraints(
          maxWidth: MediaQuery.of(context).size.width - 45,
        ),
        child: Card(
          elevation: 1,
          shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
          color: messageColor,
          margin: const EdgeInsets.symmetric(horizontal: 15, vertical: 5),
          child: Stack(
            children: [
              Padding(
                padding: const EdgeInsets.only(
                  left: 10,
                  right: 30,
                  top: 5,
                  bottom: 20,
                ),
                child: Text(
                  message,
                  style: const TextStyle(
                    fontSize: 16,
                  ),
                ),
              ),
              Positioned(
                bottom: 4,
                right: 10,
                child: Row(
                  children: [
                    Text(
                      date,
                      style:const TextStyle(
                        fontSize: 13,
                        color: Colors.white60,
                      ),
                    ),
                    const SizedBox(
                      width: 5,
                    ),
                    const Icon(
                      Icons.done_all,
                      size: 20,
                      color: Colors.white60,
                    ),
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

yukarısı MyMessageCard bu widget 2 tane şey bekleyecek biri mesajlar diğeri de tarih. Daha sonra Card döndürüyoruz bir hizalama yaptıktan sonra bu card widgetında biraz ayarlamalar yapıyoruz elevation, shape gibi ardından Stack widgetını döndürüyor bu card çünkü üst üste kullanacağımız bir yapı var. Yan yana olacağı için Row içerisinde Text ve SizedBox ile küçük bir alan verip Icon'u da ekliyoruz. Ayarlamaları yaparken MediaQuery.of(context) kullanarak sizeı çekebiliyoruz düzgün şekilde. sender_message_card.dart dosyası da benzer kodlar fakat hizalamayı centerLeft yapıyoruz.

Kod:
import 'package:flutter/material.dart';
import 'package:whatsapp_ui/colors.dart';

class SenderMessageCard extends StatelessWidget {
  const SenderMessageCard({
    Key? key,
    required this.message,
    required this.date,
  }) : super(key: key);
  final String message;
  final String date;

  @override
  Widget build(BuildContext context) {
    return Align(
      alignment: Alignment.centerLeft,
      child: ConstrainedBox(
        constraints: BoxConstraints(
          maxWidth: MediaQuery.of(context).size.width - 45,
        ),
        child: Card(
          elevation: 1,
          shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
          color: senderMessageColor,
          margin: const EdgeInsets.symmetric(horizontal: 15, vertical: 5),
          child: Stack(
            children: [
              Padding(
                padding: const EdgeInsets.only(
                  left: 10,
                  right: 30,
                  top: 5,
                  bottom: 20,
                ),
                child: Text(
                  message,
                  style: const TextStyle(
                    fontSize: 16,
                  ),
                ),
              ),
              Positioned(
                bottom: 2,
                right: 10,
                child: Text(
                  date,
                  style: TextStyle(
                    fontSize: 13,
                    color: Colors.grey[600],
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Sonuç olarak responsive bir tasarım elde ediyoruz. info.dart taki dummy için tekrardan inceleyin:

Kod:
const info = [
  {
    'name': 'Mirliva',
    'message': '200 ile gidiyordu ama kurtulamadı elimden',
    'time': '3:53 pm',
    'profilePic': 'https://www.turkhackteam.org/data/avatars/l/785/785382.jpg?1715039299',
  },
  {
    'name': 'Türk',
    'message': '35 yaşında gösteriyorsun',
    'time': '2:25 pm',
    'profilePic': 'https://www.turkhackteam.org/data/avatars/l/908/908744.jpg?1714607047',
  },
  {
    'name': 'Enistein',
    'message': 'krav maga biliyorum hoaydarı teklerim',
    'time': '1:03 pm',
    'profilePic': 'https://www.turkhackteam.org/data/avatars/l/664/664840.jpg?1675139284',
  },
  {
    'name': 'Reina',
    'message': 'ahahaha',
    'time': '12:06 pm',
    'profilePic': 'https://www.turkhackteam.org/data/avatars/l/879/879110.jpg?1661379848',
  },
  {
    'name': 'WHITERUBY',
    'message': 'ondan sonra işte yeter artık lan bu eğitim dedim',
    'time': '12:06 pm',
    'profilePic': 'https://www.turkhackteam.org/data/avatars/m/997/997470.jpg?1714059680',
  },
  {
    'name': 'musileno',
    'message': 'ne yaptın usta nasılsın iyi misin :)',
    'time': '2007',
    'profilePic': 'https://resmim.net/cdn/2024/05/07/fsejCR.jpg',
  },
  {
    'name': 'HydraThalles',
    'message': 'Omen ile yine kanserledim',
    'time': 'Premier',
    'profilePic': 'https://www.turkhackteam.org/data/avatars/l/806/806676.jpg?1652131820',
  }
];

const first = {
  'name': 'Enistein',
  'message': 'krav maga biliyorum hoaydarı teklerim',
  'time': '1:03 pm',
  'profilePic': 'https://www.turkhackteam.org/data/avatars/l/664/664840.jpg?1675139284',
};

const messages = [
  {"isMe": false, "text": "dediğim gibi işte ondan sonra google'ı hackledim", "time": "10:00 am"},
  {"isMe": true, "text": "oha nasıl ya", "time": "11:00 am"},
  {"isMe": false, "text": "hoaydar napıyor gelsin şiir okusun", "time": "11:01 am"},
  {"isMe": true, "text": "bilmiyorum da geçen seni alırım dediydi", "time": "11:01 am"},
  {"isMe": false, "text": "yaklaşamaz krav maga var bende", "time": "11:03 am"},
  {"isMe": true, "text": "belli olmaz ya", "time": "11:04 am"},
  {"isMe": false, "text": "krav maga biliyorum hoaydarı teklerim direkt", "time": "11:05 am"},

];

fsOT1C.gif


benzer şekilde chat screen'i içerisinde hem web için hem mobil için text input alanı için widgetlar yazıp tamamlayabilirsiniz. Bir sonraki konuda görüşmek üzere

<3 Gauloran



harika olmuş hocam elinize sağlık. Mesajlar beni bitirdi ama omen konusunda biraz gelişmeniz lazım hocam :)
 

Ogehan

Asistan Moderatör
5 Haz 2016
2,059
2
169
</>
Hocam ellerinize sağlık yazılımı bitirince bu mesajlar otomatik olarak geliyormu :D
 
Üst

Turkhackteam.org internet sitesi 5651 sayılı kanun’un 2. maddesinin 1. fıkrasının m) bendi ile aynı kanunun 5. maddesi kapsamında "Yer Sağlayıcı" konumundadır. İçerikler ön onay olmaksızın tamamen kullanıcılar tarafından oluşturulmaktadır. Turkhackteam.org; Yer sağlayıcı olarak, kullanıcılar tarafından oluşturulan içeriği ya da hukuka aykırı paylaşımı kontrol etmekle ya da araştırmakla yükümlü değildir. Türkhackteam saldırı timleri Türk sitelerine hiçbir zararlı faaliyette bulunmaz. Türkhackteam üyelerinin yaptığı bireysel hack faaliyetlerinden Türkhackteam sorumlu değildir. Sitelerinize Türkhackteam ismi kullanılarak hack faaliyetinde bulunulursa, site-sunucu erişim loglarından bu faaliyeti gerçekleştiren ip adresini tespit edip diğer kanıtlarla birlikte savcılığa suç duyurusunda bulununuz.