To achieve this functionality first you need to review the flatrate shipping model.
vendor/magento/module-offline-shipping/Model/Carrier/Flatrate.php
There are 4 main functions which you need to review.
1) Magento\OfflineShipping\Model\Carrier::collectRates
2) Magento\OfflineShipping\Model\Carrier::getFreeBoxesCount
3) Magento\OfflineShipping\Model\Carrier::getFreeBoxesCountFromChildren
4) Magento\OfflineShipping\Model\Carrier::getShippingPrice
Because when you are creating a custom shipping method you need to keep in mind about all product types.
Also this should work with whole cart and particular item type discount.
Basically logic for free shipping is working as below.
- when cart rule is applied and product is eligible for the free shipping then Magento set free_shipping flag 1 in the quote_item table.
- when collectRates() method call for the shipping price then it ignore items which have free_shipping flag 1 and calculate shipping rate accordingly.
I hope this will help you to achieve your functionality.
Thanks